{"id":18735639,"url":"https://github.com/rdmurphy/frames","last_synced_at":"2025-07-08T19:39:17.043Z","repository":{"id":33941891,"uuid":"163441258","full_name":"rdmurphy/frames","owner":"rdmurphy","description":"🖼 A minimalistic take on responsive iframes in the spirit of Pym.js.","archived":false,"fork":false,"pushed_at":"2023-01-07T04:01:03.000Z","size":2377,"stargazers_count":26,"open_issues_count":10,"forks_count":7,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-06-29T04:46:21.719Z","etag":null,"topics":["amphtml","iframes","journalism","responsive"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@newswire/frames","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rdmurphy.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-12-28T19:02:54.000Z","updated_at":"2024-10-02T00:03:04.000Z","dependencies_parsed_at":"2023-01-15T03:30:23.781Z","dependency_job_id":null,"html_url":"https://github.com/rdmurphy/frames","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/rdmurphy/frames","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fframes","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fframes/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fframes/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fframes/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rdmurphy","download_url":"https://codeload.github.com/rdmurphy/frames/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rdmurphy%2Fframes/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":264335093,"owners_count":23592539,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amphtml","iframes","journalism","responsive"],"created_at":"2024-11-07T15:17:37.392Z","updated_at":"2025-07-08T19:39:17.007Z","avatar_url":"https://github.com/rdmurphy.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003ch1 align=\"center\"\u003e\n  @newswire/frames\n\u003c/h1\u003e\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.org/package/@newswire/frames\"\u003e\u003cimg src=\"https://badgen.net/npm/v/@newswire/frames\" alt=\"npm\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/rdmurphy/frames/actions/workflows/ci.yaml\"\u003e\u003cimg src=\"https://github.com/rdmurphy/frames/actions/workflows/ci.yaml/badge.svg\" alt=\"ci\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://unpkg.com/@newswire/frames/dist/frames.umd.js\"\u003e\u003cimg src=\"https://badgen.net/badgesize/gzip/https://unpkg.com/@newswire/frames/dist/frames.umd.js\" alt=\"gzip size\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://unpkg.com/@newswire/frames/dist/frames.umd.js\"\u003e\u003cimg src=\"https://badgen.net/badgesize/brotli/https://unpkg.com/@newswire/frames/dist/frames.umd.js\" alt=\"brotli size\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://packagephobia.now.sh/result?p=@newswire/frames\"\u003e\u003cimg src=\"https://badgen.net/packagephobia/install/@newswire/frames\" alt=\"install size\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n`@newswire/frames` is a minimalistic take on responsive iframes in the spirit of [Pym.js](http://blog.apps.npr.org/pym.js/).\n\n## Key features\n\n- 🐜 **~1 kilobyte** gzipped for both parent and frame code\n- 🌴 **Tree-shakable** - import only what you need for your use case\n- ⚡️ **Speaks [AMP](https://www.ampproject.org)** and is compatible with [`amp-iframe`](https://www.ampproject.org/docs/reference/components/amp-iframe)\n- 🕰 **Legacy [Pym.js](http://blog.apps.npr.org/pym.js/) support** - recognizes `height` updates from Pym.js iframes\n- 🧪 **Fully tested** in Safari, Chrome and Firefox with [Playwright](https://playwright.dev)\n\n## Supported browsers\n\n| Browser                        | Supported |\n| ------------------------------ | --------- |\n| Safari                         | ✅        |\n| Mozilla Firefox                | ✅        |\n| Google Chrome                  | ✅        |\n| Opera                          | ✅        |\n| Microsoft Edge                 | ✅        |\n| Internet Explorer 11           | ✅        |\n| Internet Explorer 10 and lower | ⛔️       |\n\n## Installation\n\n`@newswire/frames` is available via `npm`.\n\n```sh\nnpm install @newswire/frames\n```\n\nYou can also use it directly via [unpkg.com](https://unpkg.com/).\n\n```html\n\u003cscript src=\"https://unpkg.com/@newswire/frames/dist/frames.umd.js\"\u003e\u003c/script\u003e\n\u003c!-- Now available at `window.newswireFrames` --\u003e\n```\n\nYou can also import it as a module via unpkg!\n\n```html\n\u003cscript type=\"module\"\u003e\n\timport * as frames from 'https://unpkg.com/@newswire/frames/dist/index.mjs';\n\n\tframes.autoInitFrames();\n\u003c/script\u003e\n```\n\n## Usage\n\n### From the **host** page (_framer_ or _parent_)\n\nThe page that contains the embeds needs to use the `Framer` class to set up instances for each embed.\n\nAssume we have the following markup in our HTML:\n\n```html\n\u003cdiv id=\"embed-container\"\u003eLoading...\u003c/div\u003e\n```\n\nThen, in our script:\n\n```js\nimport { Framer } from '@newswire/frames';\n\nconst container = document.getElementById('embed-container');\nconst src = 'https://i-am-an-embed/';\nconst attributes = { sandbox: 'allow-scripts allow-same-origin' };\n\nconst framer = new Framer(container, { src, attributes });\n// Now the iframe has been added to the page and is listening for height changes notifications from within the iframe\n```\n\nIt is also possible to observe existing iframes on a page if the content of the frames are compatible with `@newswire/frames`. This is handy if you already have your own method to dynamically add iframes to the page, or are using a custom method to lazy load them and don't need the heavy hand of `Framer`.\n\n```js\nimport { observeIframe } from '@newswire/frames';\n\n// grab a reference to an existing iframe\nconst iframe = document.getElementById('my-embed');\n\n// returns a `unobserve()` function if you need to stop listening\nconst unobserve = observeIframe(iframe);\n\n// later, if you need to disconnect from the iframe\nunobserve();\n```\n\nPym.js had the ability to automatically initialize embeds that had matching attibutes on their container elements — `@newswire/frames` can do this as well.\n\nAssume we have the following markup in our HTML:\n\n```html\n\u003cdiv data-frame-src=\"https://i-am-an-embed/\"\u003e\u003c/div\u003e\n\u003cdiv data-frame-src=\"https://i-am-an-embed-too/\"\u003e\u003c/div\u003e\n\u003cdiv data-frame-src=\"https://i-am-an-embed-three/\"\u003e\u003c/div\u003e\n```\n\nThen in our script, we can skip the fanfare of setting up a `Framer` for each one and use the `data-frame-src` attribute to find them.\n\n```js\nimport { autoInitFrames } from '@newswire/frames';\n\n// looks for any elements with `data-frame-src` that haven't been initialized yet, and sets them up\nautoInitFrames();\n```\n\nIf you're needing to pass any of the other options to `Framer` when you're automatically creating the embeds, you can add attributes that the initializer will pick up and pass along using the `data-frame-attribute-*` prefix.\n\n```html\n\u003cdiv\n\tdata-frame-src=\"https://i-am-an-embed/\"\n\tdata-frame-attribute-sandbox=\"allow-scripts allow-same-origin\"\n\u003e\u003c/div\u003e\n\n\u003c!-- This creates... --\u003e\n\u003ciframe src=\"https://i-am-an-embed/\" sandbox=\"allow-scripts allow-same-origin\"\u003e\n\u003c/iframe\u003e\n```\n\n### From the **embedded** page (_frame_ or _child_)\n\nWhile the code to setup the host page is similar to Pym's `Parent` class, the methods for making the iframed page communicate with the host page are a little different.\n\nWant to set it and forget it? You can import a function that sets up listeners and sends the initial height of the frame's content.\n\n```js\nimport { initFrame } from '@newswire/frames';\n\n// 1. Sends the initial frame's content height\n// 2. Sets up an one-time istener to send the height on load\n// 3. Sets up a listener to send the height every time the frame resizes\n// 4. Sets up an event listener that sends the height once the parent window begins watching\ninitFrame();\n```\n\nYou can also automatically set up long polling for height changes as well.\n\n```js\nimport { initFrameAndPoll } from '@newswire/frames';\n\n// 1. Sends the initial frame's content height\n// 2. Sets up an one-time listener to send the height on load\n// 3. Sets up a listener to send the height every time the frame resizes\n// 4. Sets up an event listener that sends the height once the parent window begins watching\n// 5. Sets up an interval to send a new height update every 300ms\ninitFrameAndPoll();\n```\n\nAlternatively, you can set and use function independently depending on the needs of your frame's content.\n\n```js\nimport {\n\tsendFrameHeight,\n\tsendHeightOnLoad,\n\tsendHeightOnResize,\n\tsendHeightOnPoll,\n\tsendHeightOnFramerInit,\n} from '@newswire/frames';\n\n// 1. Sends the initial frame's content height\nsendFrameHeight();\n\n// 2. Sets up an one-time listener to send the height on load\nsendHeightOnLoad();\n\n// 3. Sets up a listener to send the height every time the frame resizes\nsendHeightOnResize();\n\n// 4. Sets up an event listener that sends the height once the parent window begins watching\nsendHeightOnFramerInit();\n\n// 5. Sets up an interval to send a new height update every 150ms\nsendHeightOnPoll(150);\n\n// 1-4 is identical to initFrame()! 1-5 is identical to initFrameAndPoll()!\n```\n\nTypically using `initFrame()` will be enough, but if you have code that will potentially change the height of the frame's content (like with an `\u003cinput\u003e` or `\u003cbutton\u003e` press) and would rather not use polling, you can use `sendFrameHeight()` to manually recalculate and send an update to the parent page.\n\n## Legacy Pym.js support\n\n`@newswire/frames` (`1.0.0+`) can be a drop-in replacement for `Pym.js` on host/parent pages, but it only understands `height` events sent from Pym.js-powered child frames. All other events **will be ignored**. Unless you were doing something exotic or extremely bespoke with Pym.js odds are it will work!\n\n## API\n\n\u003c!-- Generated by documentation.js. Update this documentation by updating the source code. --\u003e\n\n#### Table of Contents\n\n- [observeIframe](#observeiframe)\n  - [Parameters](#parameters)\n  - [Examples](#examples)\n- [FramerOptions](#frameroptions)\n  - [Properties](#properties)\n- [Framer](#framer)\n  - [Parameters](#parameters-1)\n- [autoInitFrames](#autoinitframes)\n  - [Examples](#examples-1)\n- [sendFrameHeight](#sendframeheight)\n  - [Parameters](#parameters-2)\n  - [Examples](#examples-2)\n- [sendHeightOnLoad](#sendheightonload)\n  - [Examples](#examples-3)\n- [sendHeightOnResize](#sendheightonresize)\n  - [Examples](#examples-4)\n- [sendHeightOnFramerInit](#sendheightonframerinit)\n  - [Examples](#examples-5)\n- [sendHeightOnPoll](#sendheightonpoll)\n  - [Parameters](#parameters-3)\n  - [Examples](#examples-6)\n- [initFrame](#initframe)\n  - [Examples](#examples-7)\n- [initFrameAndPoll](#initframeandpoll)\n  - [Parameters](#parameters-4)\n  - [Examples](#examples-8)\n\n### observeIframe\n\nAdds an event listener to an existing iframe for receiving height change\nmessages. Also tells the iframe that we're listening and requests the\ninitial height. Returns an `unobserve()` function for later removing the\nlistener.\n\n#### Parameters\n\n- `iframe` **[HTMLIFrameElement](https://developer.mozilla.org/docs/Web/API/HTMLIFrameElement)** the iframe to observe\n\n#### Examples\n\n```javascript\n// grab a reference to an existing iframe\nconst iframe = document.getElementById('my-embed');\n\n// returns a `unobserve()` function if you need to stop listening\nconst unobserve = observeIframe(iframe);\n\n// later, if you need to disconnect from the iframe\nunobserve();\n```\n\n### FramerOptions\n\nType: [object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)\n\n#### Properties\n\n- `src` **([string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String) | null)?** the URL to set as the `src` of the iframe\n- `attributes` **Record\u003c[string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String), [string](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String)\u003e?** any attributes to add to the iframe itself\n\n### Framer\n\nThe Framer function to be called in the host page. A wrapper around\ninteractions with a created iframe. Returns a `remove()` function for\ndisconnecting the event listener and removing the iframe from the DOM.\n\n#### Parameters\n\n- `container` **[Element](https://developer.mozilla.org/docs/Web/API/Element)** the containing DOM element for the iframe\n- `options` **[FramerOptions](#frameroptions)** (optional, default `{}`)\n\n  - `options.attributes`\n  - `options.src`\n\n### autoInitFrames\n\nAutomatically initializes any frames that have not already been\nauto-activated.\n\n#### Examples\n\n```javascript\n// sets up all frames that have not been initialized yet\nautoInitFrames();\n```\n\n### sendFrameHeight\n\nSends the current document's height or provided value to the host window\nusing postMessage.\n\n#### Parameters\n\n- `height` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** The height to pass to the host page, is determined automatically if not passed (optional, default `getDocumentHeight()`)\n\n#### Examples\n\n```javascript\n// Uses the document's height to tell the host page\nsendFrameHeight();\n\n// Pass a height you've determined in another way\nsendFrameHeight(500);\n```\n\nReturns **void**\n\n### sendHeightOnLoad\n\nSets up an event listener for the load event that sends the new frame\nheight to the host. Automatically removes itself once fired.\n\n#### Examples\n\n```javascript\n// once the frame's load event is fired, tell the host page its new height\nsendHeightOnLoad();\n```\n\nReturns **void**\n\n### sendHeightOnResize\n\nSets up an event listener for the resize event that sends the new frame\nheight to the host.\n\n#### Examples\n\n```javascript\n// every time the frame is resized, tell the host page what its new height is\nsendHeightOnResize();\n```\n\nReturns **void**\n\n### sendHeightOnFramerInit\n\nSets up an event listener for a message from the parent window that it is\nnow listening for messages from this iframe, and tells it the iframe's height\nat that time. This makes it possible to delay observing an iframe (e.g. when\nlazy loading) but trust the parent will get the current height ASAP.\n\n#### Examples\n\n```javascript\n// as soon as a Framer connects, tell the host page what the current height is\nsendHeightOnFramerInit();\n```\n\nReturns **void**\n\n### sendHeightOnPoll\n\nSends height updates to the host page on an interval.\n\n#### Parameters\n\n- `delay` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** How long to set the interval (optional, default `300`)\n\n#### Examples\n\n```javascript\n// will call sendFrameHeight every 300ms\nsendHeightOnPoll();\n\n// will call sendFrameHeight every 150ms\nsendHeightOnPoll(150);\n```\n\nReturns **void**\n\n### initFrame\n\nA helper for running the standard functions for setting up a frame.\n\nAutomatically calls an `sendFrameHeight`, `sendHeightOnLoad`, `sendHeightOnResize`\nand `sendHeightOnFramerInit`.\n\n#### Examples\n\n```javascript\ninitFrame();\n```\n\nReturns **void**\n\n### initFrameAndPoll\n\nCalls `initFrame` to setup a frame, then initializes a poller to continue to update on an interval.\n\n#### Parameters\n\n- `delay` **[number](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number)?** An optional custom delay to pass to sendHeightOnPoll\n\n#### Examples\n\n```javascript\n// calls initFrame, then calls sendHeightOnPoll\ninitFrameAndPoll();\n```\n\nReturns **void**\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdmurphy%2Fframes","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frdmurphy%2Fframes","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frdmurphy%2Fframes/lists"}