CMP001

Image Comparison Slider

Interactive slider for comparing before/after images

JavaScript
Font Awesome Included: This snippet uses Font Awesome icons. The required stylesheet is automatically included at the top of the code block. If you are going to use this snippet in your project, add the stylesheet to your <head>.

Live Preview

Before image
BEFORE
After image
AFTER

Code

HTML
<!-- Font Awesome Icons; Make sure to add it in the <head> tag -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  
<div class="image-comparison">
  <div class="comparison-container">
    <div class="comparison-before">
      <img src="path/to/before-image.jpg" alt="Before image">
      <div class="comparison-label">BEFORE</div>
    </div>
    <div class="comparison-after">
      <img src="path/to/after-image.jpg" alt="After image">
      <div class="comparison-label">AFTER</div>
    </div>
    <div class="comparison-handle">
      <div class="handle-circle">
        <i class="fas fa-chevron-left"></i>
        <i class="fas fa-chevron-right"></i>
      </div>
    </div>
  </div>
</div>
CSS
/* Image Comparison Slider Base Styles */
.image-comparison {
  width: 100%;
  max-width: 800px;
  margin: 0 auto;
  font-family: Arial, sans-serif;
}

.comparison-container {
  position: relative;
  width: 100%;
  height: 0;
  padding-bottom: 66.66%; /* 3:2 aspect ratio, adjust as needed */
  overflow: hidden;
  border-radius: 8px;
  box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1);
}

.comparison-before,
.comparison-after {
  position: absolute;
  top: 0;
  width: 100%;
  height: 100%;
}

.comparison-before {
  left: 0;
  z-index: 1;
}

.comparison-after {
  left: 0;
  z-index: 2;
  width: 50%; /* Initial position - half-way */
  overflow: hidden;
}

.comparison-before img,
.comparison-after img {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.comparison-after img {
  width: 200%; /* Ensure the width is double */
  max-width: none;
}

/* Handle design */
.comparison-handle {
  position: absolute;
  top: 0;
  left: 50%; /* Initial position - half-way */
  height: 100%;
  width: 2px;
  background-color: white;
  z-index: 3;
  transform: translateX(-50%);
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  cursor: col-resize;
}

.handle-circle {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 40px;
  height: 40px;
  background-color: white;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 5px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
  transition: transform 0.2s ease;
}

.handle-circle i {
  font-size: 12px;
  color: #555;
}

.comparison-handle:hover .handle-circle {
  transform: translate(-50%, -50%) scale(1.1);
}

.comparison-handle:active .handle-circle {
  transform: translate(-50%, -50%) scale(0.95);
}

/* Labels */
.comparison-label {
  position: absolute;
  top: 15px;
  padding: 5px 10px;
  color: white;
  font-size: 12px;
  font-weight: bold;
  background-color: rgba(0, 0, 0, 0.5);
  border-radius: 4px;
  letter-spacing: 1px;
}

.comparison-before .comparison-label {
  left: 15px;
}

.comparison-after .comparison-label {
  right: 15px;
}

/* Responsive styles */
@media (max-width: 768px) {
  .handle-circle {
    width: 36px;
    height: 36px;
  }

  .handle-circle i {
    font-size: 10px;
  }

  .comparison-label {
    font-size: 10px;
    padding: 4px 8px;
  }
}

@media (max-width: 480px) {
  .handle-circle {
    width: 32px;
    height: 32px;
  }

  .comparison-label {
    top: 10px;
    font-size: 9px;
  }

  .comparison-before .comparison-label {
    left: 10px;
  }

  .comparison-after .comparison-label {
    right: 10px;
  }
}
JavaScript
document.addEventListener('DOMContentLoaded', function() {
  // Get all comparison sliders on the page
  const sliders = document.querySelectorAll('.image-comparison');
  
  sliders.forEach(slider => {
    const container = slider.querySelector('.comparison-container');
    const before = slider.querySelector('.comparison-before');
    const after = slider.querySelector('.comparison-after');
    const handle = slider.querySelector('.comparison-handle');
    
    let isResizing = false;
    
    // Handle mouse/touch events
    handle.addEventListener('mousedown', startResize);
    handle.addEventListener('touchstart', startResize);
    
    // Mouse events
    document.addEventListener('mousemove', resizeImages);
    document.addEventListener('mouseup', stopResize);
    
    // Touch events
    document.addEventListener('touchmove', resizeImages);
    document.addEventListener('touchend', stopResize);
    
    // Initialize slider position on load (50%)
    updateSliderPosition(50);
    
    // Start resizing
    function startResize(e) {
      e.preventDefault();
      isResizing = true;
      
      // Add active class for visual feedback
      container.classList.add('active');
    }
    
    // Resize images based on mouse/touch position
    function resizeImages(e) {
      if (!isResizing) return;
      
      // Get container dimensions and position
      const containerRect = container.getBoundingClientRect();
      
      // Calculate cursor position as a percentage of the container width
      let clientX = e.clientX;
      if (e.type === 'touchmove') {
        clientX = e.touches[0].clientX;
      }
      
      const position = ((clientX - containerRect.left) / containerRect.width) * 100;
      
      // Restrict position between 0% and 100%
      const clampedPosition = Math.min(Math.max(position, 0), 100);
      
      // Update the slider position
      updateSliderPosition(clampedPosition);
    }
    
    // Update slider position
    function updateSliderPosition(percentage) {
      // Update after image width
      after.style.width = `${percentage}%`;
      
      // Update handle position
      handle.style.left = `${percentage}%`;
    }
    
    // Stop resizing
    function stopResize() {
      isResizing = false;
      container.classList.remove('active');
    }
    
    // Keyboard accessibility
    handle.setAttribute('tabindex', '0');
    handle.setAttribute('role', 'slider');
    handle.setAttribute('aria-label', 'Image comparison slider');
    handle.setAttribute('aria-valuemin', '0');
    handle.setAttribute('aria-valuemax', '100');
    handle.setAttribute('aria-valuenow', '50');
    
    handle.addEventListener('keydown', function(e) {
      let newPosition = parseInt(handle.getAttribute('aria-valuenow'));
      
      // Move 5% with arrow keys
      if (e.key === 'ArrowLeft') {
        newPosition = Math.max(0, newPosition - 5);
        e.preventDefault();
      } else if (e.key === 'ArrowRight') {
        newPosition = Math.min(100, newPosition + 5);
        e.preventDefault();
      }
      
      updateSliderPosition(newPosition);
      handle.setAttribute('aria-valuenow', newPosition);
    });
  });
});