{"id":13397857,"url":"https://github.com/francoischalifour/medium-zoom","last_synced_at":"2025-05-13T18:12:33.477Z","repository":{"id":37382226,"uuid":"64253557","full_name":"francoischalifour/medium-zoom","owner":"francoischalifour","description":"🔎🖼 A JavaScript library for zooming images like Medium","archived":false,"fork":false,"pushed_at":"2024-07-24T21:21:36.000Z","size":23648,"stargazers_count":3804,"open_issues_count":48,"forks_count":165,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-05-12T02:40:05.371Z","etag":null,"topics":["event","image","image-zoom","javascript","medium","no-dependencies","performance","picture","ux","vanilla","vanilla-javascript","zoom","zoomable","zooming"],"latest_commit_sha":null,"homepage":"https://medium-zoom.francoischalifour.com","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/francoischalifour.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-07-26T20:48:35.000Z","updated_at":"2025-05-11T21:56:50.000Z","dependencies_parsed_at":"2023-01-21T05:17:28.616Z","dependency_job_id":"77f172ac-eb6b-474d-9123-ebef872641c4","html_url":"https://github.com/francoischalifour/medium-zoom","commit_stats":{"total_commits":357,"total_committers":17,"mean_commits":21.0,"dds":"0.21288515406162467","last_synced_commit":"b4ec7c67dc5114f8ae6564f9fbe6589b777bf9e5"},"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoischalifour%2Fmedium-zoom","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoischalifour%2Fmedium-zoom/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoischalifour%2Fmedium-zoom/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/francoischalifour%2Fmedium-zoom/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/francoischalifour","download_url":"https://codeload.github.com/francoischalifour/medium-zoom/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254000858,"owners_count":21997442,"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":["event","image","image-zoom","javascript","medium","no-dependencies","performance","picture","ux","vanilla","vanilla-javascript","zoom","zoomable","zooming"],"created_at":"2024-07-30T18:01:49.207Z","updated_at":"2025-05-13T18:12:33.446Z","avatar_url":"https://github.com/francoischalifour.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","目录"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://medium-zoom.francoischalifour.com\"\u003e\u003cimg src=\"logo.svg\" alt=\"Demo\" width=\"64\"\u003e\u003c/a\u003e\n  \u003ch3 align=\"center\"\u003emedium-zoom\u003c/h3\u003e\n  \u003cp align=\"center\"\u003eA JavaScript library for zooming images like Medium\u003c/p\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://www.npmjs.com/package/medium-zoom\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/v/medium-zoom.svg?style=flat-square\" alt=\"version\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/francoischalifour/medium-zoom/blob/master/LICENSE\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/l/medium-zoom.svg?style=flat-square\" alt=\"MIT license\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"http://npmcharts.com/compare/medium-zoom\"\u003e\n    \u003cimg src=\"https://img.shields.io/npm/dm/medium-zoom.svg?style=flat-square\" alt=\"downloads\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://unpkg.com/medium-zoom/dist/\"\u003e\n    \u003cimg src=\"http://img.badgesize.io/https://unpkg.com/medium-zoom/dist/medium-zoom.min.js?compression=gzip\u0026label=gzip%20size\u0026style=flat-square\" alt=\"gzip size\"\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/francoischalifour/medium-zoom/blob/master/package.json\"\u003e\n    \u003cimg src=\"https://img.shields.io/badge/dependencies-none-lightgrey.svg?style=flat-square\" alt=\"no dependencies\"\u003e\n  \u003c/a\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://medium-zoom.francoischalifour.com\"\u003e\n    \u003cimg src=\"https://user-images.githubusercontent.com/6137112/43369906-7623239a-9376-11e8-978b-6e089be499fb.gif\" alt=\"Medium Zoom Demo\"\u003e\n  \u003c/a\u003e\n  \u003cbr\u003e\n  \u003cbr\u003e\n  \u003cstrong\u003e\n  \u003ca href=\"https://codesandbox.io/s/github/francoischalifour/medium-zoom/tree/master/website\"\u003e🔬 Playground\u003c/a\u003e ・\n  \u003ca href=\"https://medium-zoom.francoischalifour.com\"\u003e🔎 Demo\u003c/a\u003e ・\n  \u003ca href=\"https://medium-zoom.francoischalifour.com/storybook\"\u003e📚 Storybook\u003c/a\u003e\n  \u003c/strong\u003e\n\u003c/p\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003cstrong\u003eContents\u003c/strong\u003e\u003c/summary\u003e\n\n\u003c!--\nGenerate the table of contents using:\n\n```\nnpx doctoc README.md --maxlevel 3\n```\n--\u003e\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n- [Features](#features)\n- [Installation](#installation)\n- [Usage](#usage)\n- [API](#api)\n  - [Selectors](#selectors)\n  - [Options](#options)\n  - [Methods](#methods)\n  - [Attributes](#attributes)\n  - [Events](#events)\n- [Framework integrations](#framework-integrations)\n- [Examples](#examples)\n- [Debugging](#debugging)\n- [Browser support](#browser-support)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\u003c/details\u003e\n\n## Features\n\n- 📱 **Responsive** — scale on mobile and desktop\n- 🚀 **Performant and lightweight** — optimized to reach 60 [fps](https://en.wikipedia.org/wiki/Framerate)\n- ⚡️ **High definition support** — load the HD version of your image on zoom\n- 🔎 **Flexibility** — apply the zoom to a selection of images\n- 🖱 **Mouse, keyboard and gesture friendly** — click anywhere, press a key or scroll away to close the zoom\n- 🎂 **Event handling** — trigger events when the zoom enters a new state\n- 📦 **Customization** — set your own margin, background and scroll offset\n- 🔧 **Pluggable** — add your own features to the zoom\n- 💎 **Custom templates** — extend the default look to match the UI of your app\n- 🔌 [**Framework agnostic**](#framework-integrations) — works with React, Vue, Angular, Svelte, Solid, etc.\n\n## Installation\n\nThe module is available on the [npm](https://www.npmjs.com) registry.\n\n```sh\nnpm install medium-zoom\n# or\nyarn add medium-zoom\n```\n\n###### Download\n\n- [Normal](https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.js)\n- [Minified](https://cdn.jsdelivr.net/npm/medium-zoom/dist/medium-zoom.min.js)\n\n###### CDN\n\n- [jsDelivr](https://www.jsdelivr.com/package/npm/medium-zoom)\n- [unpkg](https://unpkg.com/medium-zoom/)\n- [esm.sh](https://esm.sh/medium-zoom)\n\n## Usage\n\n\u003e [Try it out in the browser](https://codesandbox.io/s/github/francoischalifour/medium-zoom/tree/master/website)\n\nImport the library as a module:\n\n```js\nimport mediumZoom from 'medium-zoom'\n```\n\nOr import the library with a script tag:\n\n```html\n\u003cscript src=\"node_modules/medium-zoom/dist/medium-zoom.min.js\"\u003e\u003c/script\u003e\n```\n\nThat's it! You don't need to import any CSS styles.\n\nAssuming you add the `data-zoomable` attribute to your images:\n\n```js\nmediumZoom('[data-zoomable]')\n```\n\n\u003e [!TIP]\n\u003e If you want to control when to inject the Medium Zoom CSS styles, you can use the pure JavaScript bundle:\n\u003e\n\u003e ```js\n\u003e import mediumZoom from 'medium-zoom/dist/pure'\n\u003e import 'medium-zoom/dist/style.css'\n\u003e ```\n\n## API\n\n```ts\nmediumZoom(selector?: string | HTMLElement | HTMLElement[] | NodeList, options?: object): Zoom\n```\n\n### Selectors\n\nThe selector allows attaching images to the zoom. It can be of the following types:\n\n- [CSS selectors](https://developer.mozilla.org/docs/Web/CSS/CSS_Selectors)\n- [`HTMLElement`](https://developer.mozilla.org/docs/Web/API/HTMLElement)\n- [`NodeList`](https://developer.mozilla.org/docs/Web/API/NodeList)\n- [`Array`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)\n\n```js\n// CSS selector\nmediumZoom('[data-zoomable]')\n\n// HTMLElement\nmediumZoom(document.querySelector('#cover'))\n\n// NodeList\nmediumZoom(document.querySelectorAll('[data-zoomable]'))\n\n// Array\nconst images = [\n  document.querySelector('#cover'),\n  ...document.querySelectorAll('[data-zoomable]'),\n]\n\nmediumZoom(images)\n```\n\n### Options\n\nThe options enable the customization of the zoom. They are defined as an object with the following properties:\n\n| Property       | Type                                  | Default  | Description                                                                 |\n| -------------- | ------------------------------------- | -------- | --------------------------------------------------------------------------- |\n| `margin`       | `number`                              | `0`      | The space outside the zoomed image                                          |\n| `background`   | `string`                              | `\"#fff\"` | The background of the overlay                                               |\n| `scrollOffset` | `number`                              | `40`     | The number of pixels to scroll to close the zoom                            |\n| `container`    | `string` \\| `HTMLElement` \\| `object` | `null`   | The viewport to render the zoom in\u003cbr\u003e [Read more →](docs/container.md)     |\n| `template`     | `string` \\| `HTMLTemplateElement`     | `null`   | The template element to display on zoom\u003cbr\u003e [Read more →](docs/template.md) |\n\n```js\nmediumZoom('[data-zoomable]', {\n  margin: 24,\n  background: '#BADA55',\n  scrollOffset: 0,\n  container: '#zoom-container',\n  template: '#zoom-template',\n})\n```\n\n### Methods\n\n#### `open({ target?: HTMLElement }): Promise\u003cZoom\u003e`\n\nOpens the zoom and returns a promise resolving with the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.open()\n```\n\n_Emits an event [`open`](#events) on animation start and [`opened`](#events) when completed._\n\n#### `close(): Promise\u003cZoom\u003e`\n\nCloses the zoom and returns a promise resolving with the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.close()\n```\n\n_Emits an event [`close`](#events) on animation start and [`closed`](#events) when completed._\n\n#### `toggle({ target?: HTMLElement }): Promise\u003cZoom\u003e`\n\nOpens the zoom when closed / dismisses the zoom when opened, and returns a promise resolving with the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.toggle()\n```\n\n#### `attach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom`\n\nAttaches the images to the zoom and returns the zoom.\n\n```js\nconst zoom = mediumZoom()\n\nzoom.attach('#image-1', '#image-2')\nzoom.attach(\n  document.querySelector('#image-3'),\n  document.querySelectorAll('[data-zoomable]')\n)\n```\n\n#### `detach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom`\n\nReleases the images from the zoom and returns the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.detach('#image-1', document.querySelector('#image-2')) // detach two images\nzoom.detach() // detach all images\n```\n\n_Emits an event [`detach`](#events) on the image._\n\n#### `update(options: object): Zoom`\n\nUpdates the options and returns the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.update({ background: '#BADA55' })\n```\n\n_Emits an event [`update`](#events) on each image of the zoom._\n\n#### `clone(options?: object): Zoom`\n\nClones the zoom with provided options merged with the current ones and returns the zoom.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]', { background: '#BADA55' })\n\nconst clonedZoom = zoom.clone({ margin: 48 })\n\nclonedZoom.getOptions() // =\u003e { background: '#BADA55', margin: 48, ... }\n```\n\n#### `on(type: string, listener: () =\u003e void, options?: boolean | AddEventListenerOptions): Zoom`\n\nRegisters the listener on each target of the zoom.\n\nThe same `options` as [`addEventListener`](https://developer.mozilla.org/docs/Web/API/EventTarget/addEventListener#Parameters) are used.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.on('closed', event =\u003e {\n  // the image has been closed\n})\n\nzoom.on(\n  'open',\n  event =\u003e {\n    // the image has been opened (tracked only once)\n  },\n  { once: true }\n)\n```\n\nThe zoom object is accessible in `event.detail.zoom`.\n\n#### `off(type: string, listener: () =\u003e void, options?: boolean | AddEventListenerOptions): Zoom`\n\nRemoves the previously registered listener on each target of the zoom.\n\nThe same `options` as [`removeEventListener`](https://developer.mozilla.org/docs/Web/API/EventTarget/removeEventListener#Parameters) are used.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nfunction listener(event) {\n  // ...\n}\n\nzoom.on('open', listener)\n// ...\nzoom.off('open', listener)\n```\n\nThe zoom object is accessible in `event.detail.zoom`.\n\n#### `getOptions(): object`\n\nReturns the zoom options as an object.\n\n```js\nconst zoom = mediumZoom({ background: '#BADA55' })\n\nzoom.getOptions() // =\u003e { background: '#BADA55', ... }\n```\n\n#### `getImages(): HTMLElement[]`\n\nReturns the images attached to the zoom as an array of [`HTMLElement`s](https://developer.mozilla.org/docs/Web/API/HTMLElement).\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.getImages() // =\u003e [HTMLElement, HTMLElement]\n```\n\n#### `getZoomedImage(): HTMLElement`\n\nReturns the current zoomed image as an [`HTMLElement`](https://developer.mozilla.org/docs/Web/API/HTMLElement) or `null` if none.\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.getZoomedImage() // =\u003e null\nzoom.open().then(() =\u003e {\n  zoom.getZoomedImage() // =\u003e HTMLElement\n})\n```\n\n### Attributes\n\n#### `data-zoom-src`\n\nSpecifies the high definition image to open on zoom. This image loads when the user clicks on the source image.\n\n```html\n\u003cimg src=\"image-thumbnail.jpg\" data-zoom-src=\"image-hd.jpg\" alt=\"My image\" /\u003e\n```\n\n### Events\n\n| Event  | Description                                         |\n| ------ | --------------------------------------------------- |\n| open   | Fired immediately when the `open` method is called  |\n| opened | Fired when the zoom has finished being animated     |\n| close  | Fired immediately when the `close` method is called |\n| closed | Fired when the zoom out has finished being animated |\n| detach | Fired when the `detach` method is called            |\n| update | Fired when the `update` method is called            |\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.on('open', event =\u003e {\n  // track when the image is zoomed\n})\n```\n\nThe zoom object is accessible in `event.detail.zoom`.\n\n## Framework integrations\n\nMedium Zoom is a JavaScript library that can be used with any framework. Here are some integrations that you can use to get started quickly:\n\n- [React](./examples/react)\n- [React Markdown](./examples/react-markdown)\n- [Vue](./examples/vue)\n- [Svelte](./examples/svelte)\n\n## Examples\n\n\u003cdetails\u003e\n \u003csummary\u003eTrigger a zoom from another element\u003c/summary\u003e\n\n```js\nconst button = document.querySelector('[data-action=\"zoom\"]')\nconst zoom = mediumZoom('#image')\n\nbutton.addEventListener('click', () =\u003e zoom.open())\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003eTrack an event (for analytics)\u003c/summary\u003e\n\nYou can use the `open` event to keep track of how many times a user interacts with your image. This can be useful if you want to gather some analytics on user engagement.\n\n```js\nlet counter = 0\nconst zoom = mediumZoom('#image-tracked')\n\nzoom.on('open', event =\u003e {\n  console.log(`\"${event.target.alt}\" has been zoomed ${++counter} times`)\n})\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003eDetach a zoom once closed\u003c/summary\u003e\n\n```js\nconst zoom = mediumZoom('[data-zoomable]')\n\nzoom.on('closed', () =\u003e zoom.detach(), { once: true })\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003eAttach jQuery elements\u003c/summary\u003e\n\njQuery elements are compatible with `medium-zoom` once converted to an array.\n\n```js\nmediumZoom($('[data-zoomable]').toArray())\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n \u003csummary\u003eCreate a zoomable React component\u003c/summary\u003e\n\n```js\nimport React, { useRef } from 'react'\nimport mediumZoom from 'medium-zoom'\n\nexport function ImageZoom({ options, ...props }) {\n  const zoomRef = useRef(null)\n\n  function getZoom() {\n    if (zoomRef.current === null) {\n      zoomRef.current = mediumZoom(options)\n    }\n\n    return zoomRef.current\n  }\n\n  function attachZoom(image) {\n    const zoom = getZoom()\n\n    if (image) {\n      zoom.attach(image)\n    } else {\n      zoom.detach()\n    }\n  }\n\n  return \u003cimg {...props} ref={attachZoom} /\u003e\n}\n```\n\n\u003c/details\u003e\n\u003cbr\u003e\n\nYou can see [more examples](examples/) including [React](examples/react) and [Vue](examples/vue), or check out the [storybook](https://medium-zoom.francoischalifour.com/storybook).\n\n## Debugging\n\n### The zoomed image is not visible\n\nThe library doesn't provide a `z-index` value on the zoomed image to avoid conflicts with other frameworks. Some frameworks might specify a `z-index` for their elements, which makes the zoomed image not visible.\n\nIf that's the case, you can provide a `z-index` value in your CSS:\n\n```css\n.medium-zoom-overlay,\n.medium-zoom-image--opened {\n  z-index: 999;\n}\n```\n\n## Browser support\n\n| IE              | Edge            | Chrome | Firefox | Safari |\n| --------------- | --------------- | ------ | ------- | ------ |\n| 10\u003csup\u003e\\*\u003c/sup\u003e | 12\u003csup\u003e\\*\u003c/sup\u003e | 36     | 34      | 9      |\n\n\u003csup\u003e\\*\u003c/sup\u003e _These browsers require a [`template` polyfill](https://github.com/webcomponents/template) when using [custom templates](docs/template.md)_.\n\n\u003cblockquote\u003e\n  \u003cp align=\"center\"\u003e\n    Cross-browser testing is sponsored by\n  \u003c/p\u003e\n  \u003cp align=\"center\"\u003e\n    \u003ca href=\"https://www.browserstack.com\"\u003e\n      \u003cimg src=\"https://user-images.githubusercontent.com/6137112/44587083-35987000-a7b2-11e8-8e0d-8ba15de83802.png\" alt=\"BrowserStack\" height=\"35\"\u003e\n    \u003c/a\u003e\n  \u003c/p\u003e\n\u003c/blockquote\u003e\n\n## Contributing\n\n- Run `yarn` to install Node dev dependencies\n- Run `yarn start` to build the library in watch mode\n- Run `yarn run storybook` to see your changes at http://localhost:9001\n\nPlease read the [contributing guidelines](CONTRIBUTING.md) for more detailed explanations.\n\n_You can also use [npm](https://www.npmjs.com)._\n\n## License\n\nMIT © [François Chalifour](https://francoischalifour.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoischalifour%2Fmedium-zoom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffrancoischalifour%2Fmedium-zoom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffrancoischalifour%2Fmedium-zoom/lists"}