A JavaScript library for zooming images like Medium
npm install @fisch0920/medium-zoomA JavaScript library for zooming images like Medium

π¬ Playground γ»
π Demo γ»
π Storybook
Contents
- Features
- Installation
- Usage
- API
- Selectors
- Options
- Methods
- Attributes
- Events
- Examples
- Debugging
- Browser support
- Contributing
- License
- π± Responsive β _scale on mobile and desktop_
- π Performant and lightweight β _should be able to reach 60 fps_
- β‘οΈ High definition support β _load the HD version of your image on zoom_
- π Flexibility β _apply the zoom to a selection of images_
- π± Mouse, keyboard and gesture friendly β _click anywhere, press a key or scroll away to close the zoom_
- π Event handling β _trigger events when the zoom enters a new state_
- π¦ Customization β _set your own margin, background and scroll offset_
- π§ Pluggable β _add your own features to the zoom_
- π Custom templates β _extend the default look to match the UI of your app_
The module is available on the npm registry.
`sh`
npm install medium-zoomor
yarn add medium-zoom
###### Download
###### CDN
Import the library as a module:
`js`
import mediumZoom from 'medium-zoom'
Or import the library with a script tag:
`html`
That's it! You don't need to import any CSS styles.
Assuming you add the data-zoomable attribute to your images:
`js`
mediumZoom('[data-zoomable]')
`ts`
mediumZoom(selector?: string | HTMLElement | HTMLElement[] | NodeList, options?: object): Zoom
The selector allows attaching images to the zoom. It can be of the following types:
- CSS selectors
- HTMLElement
- NodeList
- Array
`js
// CSS selector
mediumZoom('[data-zoomable]')
// HTMLElement
mediumZoom(document.querySelector('#cover'))
// NodeList
mediumZoom(document.querySelectorAll('[data-zoomable]'))
// Array
const images = [
document.querySelector('#cover'),
...document.querySelectorAll('[data-zoomable]'),
]
mediumZoom(images)
`
The options enable the customization of the zoom. They are defined as an object with the following properties:
| Property | Type | Default | Description |
| -------------- | ------------------------------------- | -------- | --------------------------------------------------------------------------- |
| margin | number | 0 | The space outside the zoomed image |background
| | string | "#fff" | The background of the overlay |scrollOffset
| | number | 40 | The number of pixels to scroll to close the zoom |container
| | string \| HTMLElement \| object | null | The viewport to render the zoom intemplate
Read more β |
| | string \| HTMLTemplateElement | null | The template element to display on zoom
Read more β |
`js`
mediumZoom('[data-zoomable]', {
margin: 24,
background: '#BADA55',
scrollOffset: 0,
container: '#zoom-container',
template: '#zoom-template',
})
#### open({ target?: HTMLElement }): Promise
Opens the zoom and returns a promise resolving with the zoom.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.open()
`
_Emits an event open on animation start and opened when completed._
#### close(): Promise
Closes the zoom and returns a promise resolving with the zoom.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.close()
`
_Emits an event close on animation start and closed when completed._
#### toggle({ target?: HTMLElement }): Promise
Opens the zoom when closed / dismisses the zoom when opened, and returns a promise resolving with the zoom.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.toggle()
`
#### attach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom
Attaches the images to the zoom and returns the zoom.
`js
const zoom = mediumZoom()
zoom.attach('#image-1', '#image-2')
zoom.attach(
document.querySelector('#image-3'),
document.querySelectorAll('[data-zoomable]')
)
`
#### detach(...selectors: string[] | HTMLElement[] | NodeList[] | Array[]): Zoom
Releases the images from the zoom and returns the zoom.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.detach('#image-1', document.querySelector('#image-2')) // detach two images
zoom.detach() // detach all images
`
_Emits an event detach on the image._
#### update(options: object): Zoom
Updates the options and returns the zoom.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.update({ background: '#BADA55' })
`
_Emits an event update on each image of the zoom._
#### clone(options?: object): Zoom
Clones the zoom with provided options merged with the current ones and returns the zoom.
`js
const zoom = mediumZoom('[data-zoomable]', { background: '#BADA55' })
const clonedZoom = zoom.clone({ margin: 48 })
clonedZoom.getOptions() // => { background: '#BADA55', margin: 48, ... }
`
#### on(type: string, listener: () => void, options?: boolean | AddEventListenerOptions): Zoom
Registers the listener on each target of the zoom.
The same options as addEventListener are used.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.on('closed', event => {
// the image has been closed
})
zoom.on(
'open',
event => {
// the image has been opened (tracked only once)
},
{ once: true }
)
`
The zoom object is accessible in event.detail.zoom.
#### off(type: string, listener: () => void, options?: boolean | AddEventListenerOptions): Zoom
Removes the previously registered listener on each target of the zoom.
The same options as removeEventListener are used.
`js
const zoom = mediumZoom('[data-zoomable]')
function listener(event) {
// ...
}
zoom.on('open', listener)
// ...
zoom.off('open', listener)
`
The zoom object is accessible in event.detail.zoom.
#### getOptions(): object
Returns the zoom options as an object.
`js
const zoom = mediumZoom({ background: '#BADA55' })
zoom.getOptions() // => { background: '#BADA55', ... }
`
#### getImages(): HTMLElement[]
Returns the images attached to the zoom as an array of HTMLElements.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.getImages() // => [HTMLElement, HTMLElement]
`
#### getZoomedImage(): HTMLElement
Returns the current zoomed image as an HTMLElement or null if none.
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.getZoomedImage() // => null
zoom.open().then(() => {
zoom.getZoomedImage() // => HTMLElement
})
`
#### data-zoom-src
Specifies the high definition image to open on zoom. This image loads when the user clicks on the source image.
`html`![]()
| Event | Description |
| ------ | --------------------------------------------------- |
| open | Fired immediately when the open method is called |close
| opened | Fired when the zoom has finished being animated |
| close | Fired immediately when the method is called |detach
| closed | Fired when the zoom out has finished being animated |
| detach | Fired when the method is called |update
| update | Fired when the method is called |
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.on('open', event => {
// track when the image is zoomed
})
`
The zoom object is accessible in event.detail.zoom.
Trigger a zoom from another element
`js
const button = document.querySelector('[data-action="zoom"]')
const zoom = mediumZoom('#image')
button.addEventListener('click', () => zoom.open())
`
Track an event (for analytics)
You 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.
`js
let counter = 0
const zoom = mediumZoom('#image-tracked')
zoom.on('open', event => {
console.log("${event.target.alt}" has been zoomed ${++counter} times)`
})
Detach a zoom once closed
`js
const zoom = mediumZoom('[data-zoomable]')
zoom.on('closed', () => zoom.detach(), { once: true })
`
Attach jQuery elements
jQuery elements are compatible with medium-zoom once converted to an array.
`js`
mediumZoom($('[data-zoomable]').toArray())
Create a zoomable React component
Using React hooks
`js
import React from 'react'
import mediumZoom from 'medium-zoom'
function ImageZoom({ zoom, src, alt, background }) {
const zoomRef = React.useRef(zoom.clone({ background }))
function attachZoom(image) {
zoomRef.current.attach(image)
}
return
}
function App() {
const zoom = React.useRef(mediumZoom({ background: '#000', margin: 48 }))
render() {
return (
)
}
}
`
Using React classes
`js
import React, { Component } from 'react'
import mediumZoom from 'medium-zoom'
class ImageZoom extends Component {
zoom = this.props.zoom.clone({
background: this.props.color,
})
attachZoom = image => {
this.zoom.attach(image)
}
render() {
return (
)
}
}
class App extends Component {
zoom = mediumZoom({ background: '#000', margin: 48 })
render() {
return (
)
}
}
`
You can see more examples including React and Vue, or check out the storybook.
The zoomed image is not visible
The 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.
If that's the case, you can provide a z-index value in your CSS:
`css`
.medium-zoom-overlay,
.medium-zoom-image--opened {
z-index: 999;
}
| IE | Edge | Chrome | Firefox | Safari |
| --------------- | --------------- | ------ | ------- | ------ |
| 10\ | 12\ | 36 | 34 | 9 |
\* _These browsers require a template polyfill when using custom templates_.
Cross-browser testing is sponsored by
- Run yarn to install Node dev dependenciesyarn start
- Run to build the library in watch modeyarn run storybook` to see your changes at http://localhost:9001
- Run
Please read the contributing guidelines for more detailed explanations.
_You can also use npm._
MIT Β© FranΓ§ois Chalifour