Now.js Framework Documentation

Now.js Framework Documentation

MediaViewer

EN 15 Dec 2025 08:11

MediaViewer

Overview

MediaViewer is a component for displaying media (images, videos, YouTube) as a slideshow in Now.js Framework. It supports zoom, fullscreen, and touch gestures.

When to use:

  • Need image gallery/lightbox
  • Need video player
  • Need slideshow
  • Need media preview

Why use it:

  • ✅ Multiple media types (image, video, YouTube)
  • ✅ Slideshow with thumbnails
  • ✅ Zoom and pan
  • ✅ Touch gestures (swipe)
  • ✅ Keyboard controls
  • ✅ Fullscreen support
  • ✅ Accessibility (ARIA)

Basic Usage

HTML Declarative

<div data-component="media-viewer">
  <img src="/images/photo1.jpg" alt="Photo 1">
  <img src="/images/photo2.jpg" alt="Photo 2">
  <img src="/images/photo3.jpg" alt="Photo 3">
</div>

With Mixed Media

<div data-component="media-viewer">
  <img src="/images/photo.jpg" alt="Photo">
  <video src="/videos/clip.mp4"></video>
  <div data-youtube="dQw4w9WgXcQ"></div>
</div>

JavaScript API

const viewer = new MediaViewer({
  items: [
    { type: 'image', src: '/photo1.jpg', caption: 'Photo 1' },
    { type: 'image', src: '/photo2.jpg', caption: 'Photo 2' },
    { type: 'video', src: '/video.mp4', caption: 'Video' },
    { type: 'youtube', videoId: 'dQw4w9WgXcQ', caption: 'YouTube' }
  ],
  showThumbnails: true,
  loop: true
});

viewer.show();

Media Item Types

Type Properties
image src, caption, alt
video src, caption, poster
youtube videoId, caption
iframe src, caption
html content, caption

Options

new MediaViewer({
  // Media items array
  items: [],

  // Show thumbnail strip
  showThumbnails: true,

  // Loop slideshow
  loop: true,

  // Enable zoom on images
  enableZoom: true,

  // Auto-play slideshow
  autoPlay: false,
  autoPlayInterval: 5000,

  // Show controls
  showNavigation: true,
  showCounter: true,
  showCaption: true,

  // Keyboard shortcuts
  keyboard: true,

  // Close on backdrop click
  closeOnBackdrop: true,

  // Callbacks
  onOpen: () => {},
  onClose: () => {},
  onChange: (index) => {}
});

API Reference

new MediaViewer(options)

Create MediaViewer instance

Returns: MediaViewer

viewer.show(items?, startIndex?)

Show viewer

Parameter Type Description
items array Media items (optional)
startIndex number Starting index (default: 0)
viewer.show();
viewer.show(items, 2);  // Start at index 2

viewer.hide()

Hide viewer

viewer.next()

Go to next item

viewer.prev()

Go to previous item

viewer.goTo(index)

Go to specific index

viewer.play()

Start slideshow

viewer.stop()

Stop slideshow

Keyboard Shortcuts

Key Action
Next item
Previous item
Space Play/Pause slideshow
Escape Close viewer
+ / = Zoom in
- Zoom out
0 Reset zoom
F Toggle fullscreen

Touch Gestures

Gesture Action
Swipe Left Next item
Swipe Right Previous item
Pinch Zoom
Double-tap Toggle zoom
Swipe Down Close viewer

CSS Styling

/* Viewer container */
.media-viewer {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.95);
  z-index: 9999;
  display: flex;
  flex-direction: column;
}

.media-viewer-content {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}

/* Main media */
.media-viewer-main img,
.media-viewer-main video {
  max-width: 90%;
  max-height: 80vh;
  object-fit: contain;
}

/* Navigation */
.media-viewer-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  background: rgba(255,255,255,0.1);
  color: white;
  border: none;
  padding: 16px;
  cursor: pointer;
}

.media-viewer-prev { left: 16px; }
.media-viewer-next { right: 16px; }

/* Thumbnails */
.media-viewer-thumbnails {
  display: flex;
  gap: 8px;
  padding: 16px;
  overflow-x: auto;
  justify-content: center;
}

.media-viewer-thumb {
  width: 60px;
  height: 60px;
  object-fit: cover;
  opacity: 0.5;
  cursor: pointer;
}

.media-viewer-thumb.active {
  opacity: 1;
  outline: 2px solid white;
}

/* Caption */
.media-viewer-caption {
  text-align: center;
  color: white;
  padding: 16px;
}

/* Close button */
.media-viewer-close {
  position: absolute;
  top: 16px;
  right: 16px;
  background: none;
  border: none;
  color: white;
  font-size: 24px;
  cursor: pointer;
}

Real-World Examples

<div class="product-gallery">
  <div class="main-image" id="main-image">
    <img src="/products/shoe-1.jpg" alt="Main">
  </div>
  <div class="thumbnails" data-component="media-viewer">
    <img src="/products/shoe-1.jpg" alt="View 1">
    <img src="/products/shoe-2.jpg" alt="View 2">
    <img src="/products/shoe-3.jpg" alt="View 3">
    <video src="/products/shoe-video.mp4" poster="/products/shoe-poster.jpg"></video>
  </div>
</div>

Photo Album

// Open viewer from grid
document.querySelectorAll('.photo-grid img').forEach((img, index) => {
  img.addEventListener('click', () => {
    const items = Array.from(document.querySelectorAll('.photo-grid img')).map(img => ({
      type: 'image',
      src: img.src.replace('thumb', 'full'),
      caption: img.alt
    }));

    const viewer = new MediaViewer({ items });
    viewer.show(null, index);
  });
});
const videos = [
  { type: 'youtube', videoId: 'video1', caption: 'Tutorial Part 1' },
  { type: 'youtube', videoId: 'video2', caption: 'Tutorial Part 2' },
  { type: 'video', src: '/local-video.mp4', caption: 'Demo' }
];

const viewer = new MediaViewer({
  items: videos,
  showThumbnails: true
});

document.getElementById('watch-btn').onclick = () => viewer.show();

Common Pitfalls

⚠️ 1. YouTube Video ID

// ❌ Full URL
{ type: 'youtube', videoId: 'https://youtube.com/watch?v=abc123' }

// ✅ Video ID only
{ type: 'youtube', videoId: 'abc123' }

⚠️ 2. Image Paths

// ❌ Relative to current page
{ type: 'image', src: 'photo.jpg' }

// ✅ Absolute or from root
{ type: 'image', src: '/images/photo.jpg' }