Display card grid with hover preview images

by raviteja gunda

HTML

<div class="card-grid">
  <div class="card">
    <img src="https://images.unsplash.com/photo-1624555130581-1d9cca783bc0?q=80&w=3542&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" data-full="full1.jpg" alt="Card 1" class="previewable-image" />
    <h3>Card Title 1</h3>
    <p>Short description goes here.</p>
  </div>

  <div class="card">
    <img src="https://plus.unsplash.com/premium_photo-1683865776032-07bf70b0add1?q=80&w=3432&auto=format&fit=crop&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" data-full="full2.jpg" alt="Card 2" class="previewable-image" />
    <h3>Card Title 2</h3>
    <p>Another brief text.</p>
  </div>
  
   <div class="card">
    <img src="https://images.pexels.com/photos/15752372/pexels-photo-15752372/free-photo-of-camera-menu-on-screen.jpeg?auto=compress&cs=tinysrgb&w=1260&h=750&dpr=2" data-full="full2.jpg" alt="Card 2" class="previewable-image" />
    <h3>Card Title 2</h3>
    <p>Another brief text.</p>
  </div>

  <!-- Add more cards as needed -->
</div>

CSS

/* Grid layout */
  .card-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 16px;
    padding: 20px;
  }

  /* Card styling */
  .card {
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
    transition: transform 0.2s;
  }

  .card:hover {
    transform: translateY(-4px);
  }

  .card img {
    width: 100%;
    height: 160px;
    object-fit: cover;
    display: block;
  }

  .card h3 {
    margin: 10px;
    font-size: 1.1rem;
  }

  .card p {
    margin: 0 10px 10px;
    font-size: 0.95rem;
    color: #555;
  }

  /* Hover preview container */
  #hover-preview {
    position: absolute;
    display: none;
    pointer-events: none;
    border: 1px solid #ccc;
    background: #fff;
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
    z-index: 1000;
    max-width: 400px;
    max-height: 400px;
    overflow: hidden;
    border-radius: 6px;
    animation: fadeIn 0.2s ease-in-out;
  }

  #hover-preview img {
    width: 100%;
    height: auto;
    display: block;
  }

  /* Optional fade-in animation */
  @keyframes fadeIn {
    from {
      opacity: 0;
      transform: scale(0.95);
    }
    to {
      opacity: 1;
      transform: scale(1);
    }
  }

JavaScript

// Create the floating preview container
  const preview = document.createElement('div');
  preview.id = 'hover-preview';
  preview.innerHTML = '<img src="" alt="Full Preview">';
  document.body.appendChild(preview);

  const previewImg = preview.querySelector('img');

  // Attach events to all previewable images
  document.querySelectorAll('.previewable-image').forEach(img => {
    img.addEventListener('mouseenter', () => {
      const fullSrc = img.getAttribute('src') || img.src;
      previewImg.src = fullSrc;
      preview.style.display = 'block';
    });

    img.addEventListener('mousemove', (e) => {
      preview.style.left = (e.pageX + 15) + 'px';
      preview.style.top = (e.pageY + 15) + 'px';
    });

    img.addEventListener('mouseleave', () => {
      preview.style.display = 'none';
    });
  });