/medium-zoom

πŸ”ŽπŸ–Ό Medium zoom on your images in vanilla JavaScript

Primary LanguageJavaScriptMIT LicenseMIT

Demo

medium-zoom

Medium zoom on your images in vanilla JavaScript πŸ”Ž πŸ–Ό

version downloads MIT license no dependencies
size gzip size tested with Jest js-standard-style

medium-zoom demo

πŸ”¬ Playground ・ πŸ”Ž Demo

Table of contents

Features

  • πŸ“± 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
  • πŸ”Ž Image selection β€” apply the zoom to a selection of images
  • πŸ–± Mouse, keyboard and gesture friendly β€” click anywhere, press a key or scroll away to dismiss the zoom
  • πŸŽ‰ Event handling β€” trigger events when the zoom enters a new state
  • πŸ”§ Customization β€” set your own margin, background and scroll offset
  • πŸ’Ž Custom templates β€” extend the default look to match your UI
  • πŸ”— Link support β€” open the link of the image in a new tab when a meta key is held (⌘ or Ctrl)
  • πŸ–Ό Image opener β€” when no link, open the image source in a new tab when a meta key is held (⌘ or Ctrl)

Installation

This module is available on the npm registry, with no dependencies.

npm install --save medium-zoom
# or
yarn add medium-zoom

If you want to use the CDN version:

<script src="https://unpkg.com/medium-zoom@0/dist/medium-zoom.min.js"></script>

To use a local version, you can download the minified version of the module.

Usage

1. Import the script

You can skip this step if you use the CDN version.

Import the script:

<script src="node_modules/medium-zoom/dist/medium-zoom.min.js"></script>

Or, using imports:

import mediumZoom from 'medium-zoom'

That's it! You don't need to import any CSS styles.

2. Use the library

mediumZoom(<selector>, <options>)

By default, the zoom is applied to all scaled images (with HTML or CSS properties). You can specify the zoomable images with a CSS selector and add options.

Additionally, you can pass an HTML Element, a NodeList, an HTMLCollection or an array of images to the plugin.

// CSS selector
mediumZoom('#cover')

// HTML Element
mediumZoom(document.getElementById('cover'))

// NodeList
mediumZoom(document.querySelectorAll('[data-action="zoom"]'))

// HTMLCollection
mediumZoom(document.images)

// Array
const imagesToZoom = [
  document.querySelector('#cover'),
  ...document.querySelectorAll('[data-action="zoom"]')
]

mediumZoom(imagesToZoom)

API

Options

Options can be passed via a JavaScript object through the mediumZoom call.

Property Type Default Description
margin number 0 The space outside the zoomed image
background string "#fff" The color of the overlay
scrollOffset number 48 The number of pixels to scroll to dismiss the zoom
metaClick boolean true Enables the action on meta click (opens the link / image source)
container string|Element|object The element to render the zoom in or a viewport object. Read more β†’
template string|Element The template element to show on zoom. Read more β†’
mediumZoom('[data-action="zoom"]', {
  margin: 24,
  background: '#000',
  scrollOffset: 0,
  metaClick: false,
  container: '#zoom-container',
  template: '#zoom-template'
})

Using a custom container

The zoom is by default rendered in the window viewport. You can also render your image in any element of the DOM, or any custom coordinates with the container option.

Rendering in a DOM Element
<article>
  <p>My article...</p>
  <img src="image.jpg" alt="My image">
  <div id="zoom-container"></div>
</article>

<script>
  mediumZoom('img', {
    container: '#zoom-container' // or document.querySelector('#zoom-container')
  })
</script>
Rendering with coordinates

If you don't already have an element in your DOM to specify the position of the zoom, you can pass an object with the following number properties:

mediumZoom('img', {
  container: {
    width: 720,
    height: 480,
    top: 64,
    bottom: 64,
    right: 0,
    left: 0
  }
})

These properties behave very much like Element.getBoundingClientRect(). They will get merged with the default ones so you don't need to specify all of them.

The default width and height are window.innerWidth and window.innerHeight. Others are set to 0.

Using a custom template

You might want to render the zoom in your own template. You could reproduce zooms as seen on Facebook or Dropbox Paper. This is possible with the template option.

  1. Create a template element matching the template option value
  2. If you'd like your image to appear at a specific position in your template, specify the container option and add it in your template (#zoom-container here)
<template id="zoom-template">
  <div>
    <header>My image zoom template</header>
    <div id="zoom-container"></div>
    <aside>Comment on my image</aside>
  </div>
</template>

<script>
  mediumZoom('[data-action="zoom"]', {
    template: '#zoom-template',
    container: '#zoom-container'
  })
</script>

Go to the examples/ folder for more details.

Methods

.show()

Triggers the zoom.

const zoom = mediumZoom('#my-image')

zoom.show()

Emits an event show on animation start and shown when completed.

.hide()

Dismisses the zoom.

const zoom = mediumZoom('#my-image')

zoom.hide()

Emits an event hide on animation start and hidden when completed.

.toggle()

Shows the zoom when hidden, hides the zoom when shown.

const zoom = mediumZoom('#my-image')

zoom.toggle()

.update(<options>)

Updates and returns the options.

const zoom = mediumZoom('#my-image')

zoom.update({
  background: '#000'
})

.detach()

Releases the images related to the zoom from the plugin.

const zoom = mediumZoom('#my-image')

zoom.detach()

Emits an event detach.

.addEventListeners(type, listener)

Registers the specified listener on each target of the zoom.

const zoom = mediumZoom('[data-action="zoom"]')

zoom.addEventListeners('hidden', () => {
  // do something...
})

Data attributes

data-zoom-target

Specifies the high definition image to show on zoom. This image is loaded when the user clicks on the source image, only once.

<img
  src="image-thumb.jpg"
  data-zoom-target="image-hd.jpg"
  alt="My image"
>

Events

Event Description
show Fired immediately when the show instance method is called
shown Fired when the zoom has finished being animated
hide Fired immediately when the hide instance method is called
hidden Fired when the zoom out has finished being animated
detach Fired when the detach instance method is called
const zoom = mediumZoom('#image-tracked')

zoom.addEventListeners('show', event => {
  // do something...
})

Examples

Declare zoomable images with data attributes

A common pattern to declare images as zoomable is to use data attributes.

mediumZoom('[data-action="zoom"]')
Attach a zoom dynamically

When using a framework that mounts your component several times, you don't want to apply a zoom multiple times to the same image. You can apply the CSS selector :not(.medium-zoom-image) to your selection of images. The CSS class .medium-zoom-image is added to your image the first time medium-zoom is attached.

mediumZoom('img:not(.medium-zoom-image)')
Attach a zoom to external images
mediumZoom('img[src^="http"]')
Attach a zoom to images from a database
fetch(`https://myapi.com/posts/${postId}`)
  .then(response => response.json())
  .then(post => {
    const imagesToZoom = post.images.map(imgSrc =>
      document.querySelector(`img[src=${imgSrc}]`)
    )

    mediumZoom(imagesToZoom)
  })
Attach a zoom using React refs
import React, { Component } from 'react'
import mediumZoom from 'medium-zoom'

class App extends Component {
  attachZoom = image => {
    mediumZoom(image)
  }

  render() {
    return (
      <img src="image.jpg" alt="Image" ref={this.attachZoom} />
    )
  }
}
Trigger a zoom from another element

You sometimes want to trigger a zoom when the user clicks somewhere else.

const button = document.querySelector('#btn-zoom')
const zoom = mediumZoom('#image')

button.addEventListener('click', () => zoom.show())
Track an event (for analytics)

You can use the show 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.

let counter = 0
const zoom = mediumZoom('#image-tracked')

zoom.addEventListeners('show', event => {
  console.log(`"${event.target.alt}" has been zoomed ${++counter} times`)
})
Detach a zoom after a while
const zoom = mediumZoom('#image-detach')

setTimeout(() => {
  zoom.detach()
}, 5000)
Make a zoom clickable only once
const zoomToDetach = mediumZoom('#zoom-detach')

zoomToDetach.addEventListeners('hidden', zoomToDetach.detach)

You can see more examples including vanilla JavaScript, React βš›οΈ and Vue.

Demo

View demo πŸ”Ž, go to the examples folder or read the article.

Browser support

IE Edge Chrome Firefox Safari
10* 12* 36 34 9

* These browsers require a <template> polyfill when using custom templates.

Dev

More commands
  • Lint code: npm run lint:fix
  • Test: npm test

You can also use Yarn.

Contributing

Need more options? Send a pull request!

  1. Fork the repository
  2. Create a new branch
  3. Send a pull request πŸ‘Œ

License

MIT © François Chalifour