Is there a way to get an iframe to mimic the behavior of other media elements within a horizontal scrolling container?

Take a look at this code snippet:

$(document).ready(function() {
  $('.scrollable-area').on('wheel', function(e) {
    var scrollLeft = $(this).scrollLeft();
    var width = $(this).get(0).scrollWidth - $(this).width();
    var deltaY = e.originalEvent.deltaY;
    var deltaX = e.originalEvent.deltaX;
    var newScrollLeft = scrollLeft + deltaY + deltaX;
    if ((deltaY > 0 && newScrollLeft < width) ||
      (deltaY < 0 && newScrollLeft > 0)) {
      e.preventDefault();
    }
    if (newScrollLeft <= 0) {
      $(this).scrollLeft(0);
    } else if (newScrollLeft >= width) {
      $(this).scrollLeft(width);
    } else {
      $(this).scrollLeft(newScrollLeft);
    }
  });
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gallery-container {
  position: relative;
  width: 100%;
  height: 90vh;
  background-color: yellow;
  overflow: hidden;
}

.scrollable-area {
  width: 100%;
  height: 100%;
  overflow-x: auto;
}

.gallery-items {
  display: flex;
  min-width: 100%;
  height: 100%;
}

.gallery-item {
  flex: 0 0 auto;
  height: 100%;
  display: flex;
}

.gallery-item img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

.gallery-item iframe {
  background-color: blue;
  width: auto;
  width: 800px;
}
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

<div class="gallery-container">
  <div class="scrollable-area">
    <div class="gallery-items">
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg">
      </div>
      <div class="gallery-item">
        <iframe src="https://player.vimeo.com/video/584985260" frameborder="0" allow="fullscreen"></iframe>
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
    </div>
  </div>
</div>

When scrolling through the media gallery, you might notice that it stops when reaching an iframe. This behavior is intentional but can be improved. I've attempted to handle pointer events during scrolling, but encountered issues with video playback buttons. If anyone has insight on resizing iframes dynamically or adjusting their height within the horizontal gallery, your input would be greatly appreciated!

Answer №1

To prevent iframe pointer events, you can insert an overlay on your scrolling div and then adjust the pointer events according to mouse movements.

Inspired by trincot's response on "How to detect when mousemove has stopped"
we could implement a custom event called mousestop.

  1. We disable iframe pointer events by adding an overlay.
  2. The overlay is toggled based on mouse movements:
    if there is a pause in mouse movement (determined by setTimeout delay), we activate the overlay to block pointer events within the iframe content – which allows for wheel scroll events. Accessing iframe/slide buttons is possible while hovering over the slider wrap.
  3. Iframe dimensions are easily specified using aspect-ratio, such as 16/9.

let galleryWrap = document.querySelector('.gallery-container');


(function(mouseStopDelay) {
  var timeout;
  document.addEventListener('mousemove', function(e) {
    clearTimeout(timeout);
    timeout = setTimeout(function() {
      var event = new CustomEvent("mousestop", {
        detail: {
          clientX: e.clientX,
          clientY: e.clientY
        },
        bubbles: true,
        cancelable: true
      });
      e.target.dispatchEvent(event);
    }, mouseStopDelay);
  });
}(1000));

// Example use
galleryWrap.addEventListener('mousestop', function(e) {
  galleryWrap.classList.add('inactive')
});

galleryWrap.addEventListener('mousemove', function(e) {
  galleryWrap.classList.remove('inactive')
});

galleryWrap.addEventListener('click', function(e) {
  galleryWrap.classList.remove('inactive')
});


$(document).ready(function() {
  let galleryWrap = $('.gallery-container')
  let scrollArea = $('.scrollable-area')
  galleryWrap.on('wheel', function(e) {
    scrolling = true;
    var scrollLeft = scrollArea.scrollLeft();
    var width = scrollArea.get(0).scrollWidth - scrollArea.width();
    var deltaY = e.originalEvent.deltaY;
    var deltaX = e.originalEvent.deltaX;
    var newScrollLeft = scrollLeft + deltaY + deltaX;
    if ((deltaY > 0 && newScrollLeft < width) ||
      (deltaY < 0 && newScrollLeft > 0)) {
      e.preventDefault();
    }
    if (newScrollLeft <= 0) {
      scrollArea.scrollLeft(0);
    } else if (newScrollLeft >= width) {
      scrollArea.scrollLeft(width);
    } else {
      scrollArea.scrollLeft(newScrollLeft);
    }
  });
});
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

.gallery-container {
  position: relative;
  width: 100%;
  height: 250px;
  background-color: yellow;
  overflow: hidden;
}

.scrollable-area {
  width: 100%;
  height: 100%;
  overflow-x: auto;
}

.gallery-items {
  display: flex;
  min-width: 100%;
  height: 100%;
}

.gallery-item {
  flex: 0 0 auto;
  height: 100%;
  display: flex;
}

.gallery-item img {
  max-width: 100%;
  height: auto;
  object-fit: contain;
}

.gallery-item iframe {
  width: 100%;
  aspect-ratio: 16/9;
  display: block;
}

.scrollable-area {
  position: relative;
}

.gallery-container.inactive:after {
  content: "";
  position: absolute;
  display: block;
  left: 0;
  top: 0;
  right: 0;
  width: 100%;
  height: 100%;
  background: red;
  opacity: 0.1;
  z-index: 100;
}
<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

<div class="gallery-container inactive">
  <div class="scrollable-area">
    <div class="gallery-items">

      <div class="gallery-item">
        <iframe frameborder="0" scrolling="no" srcdoc='<html><body style="margin:0; padding:0;"><video style="margin:0; padding:0; width:100%; aspect-ratio:16/9" controls muted playsinline controls preload="meta"><source src="https://cdn.jsdelivr.net/npm/<a href="/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="e2808b85cf80978189cf80978c8c9bcfd3d2dad292a2d2ccd2ccd4">[email protected]</a>/video.mp4#t0" type="video/mp4"></video></html></body>'></iframe>
      </div>

      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg">
      </div>

      <div class="gallery-item">
        <img src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG">
      </div>
    </div>
  </div>
</div>

To demonstrate the toggle behavior of the overlay, I have added a semi-transparent reddish tint.

Answer №2

Revise your jQuery script to manage scrolling events for both images and iframes, while also adjusting the size of iframes dynamically to accommodate their content and adapting the height of the horizontal gallery accordingly.

<script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
<script>
  $(document).ready(function() {
    $('.scrollable-area').on('wheel', function(e) {
      var scrollLeft = $(this).scrollLeft();
      var width = $(this).get(0).scrollWidth - $(this).width();
      var deltaY = e.originalEvent.deltaY;
      var deltaX = e.originalEvent.deltaX;
      var newScrollLeft = scrollLeft + deltaY + deltaX;
      if ((deltaY > 0 && newScrollLeft < width) ||
        (deltaY < 0 && newScrollLeft > 0)) {
        e.preventDefault();
      }
      if (newScrollLeft <= 0) {
        $(this).scrollLeft(0);
      } else if (newScrollLeft >= width) {
        $(this).scrollLeft(width);
      } else {
        $(this).scrollLeft(newScrollLeft);
      }
    });

    // Resize iframes to fit their content
    function resizeIframes() {
      $('.gallery-item iframe').each(function() {
        this.height = this.contentWindow.document.body.scrollHeight + 'px';
      });
    }

    // Invoke the function upon initial loading and window resizing
    resizeIframes();
    $(window).resize(function() {
      resizeIframes();
    });
  });
</script>

Answer №3

To achieve this, utilizing the setTimeout function could be a viable option. While it may not be considered the most optimal approach, if you have the ability to update the source website of the iframe, then there should be no issue.

$(document).ready(function () {
        $(".scrollable-area").on("wheel", function (e) {
          var scrollLeft = $(this).scrollLeft()
          var width = $(this).get(0).scrollWidth - $(this).width()
          var deltaY = e.originalEvent.deltaY
          var deltaX = e.originalEvent.deltaX
          var newScrollLeft = scrollLeft + deltaY + deltaX
          if ((deltaY > 0 && newScrollLeft < width) || (deltaY < 0 && newScrollLeft > 0)) {
            e.preventDefault()
          }
          if (newScrollLeft <= 0) {
            $(this).scrollLeft(0)
          } else if (newScrollLeft >= width) {
            $(this).scrollLeft(width)
          } else {
            $(this).scrollLeft(newScrollLeft)
          }
        })

        let frameBack = document.querySelector(".background")
        frameBack.addEventListener("mousemove", (e) => {
          frameBack.classList.remove("z-1")
          frameBack.classList.add("-z-1")
          setTimeout(() => {
            frameBack.classList.remove("-z-1")
            frameBack.classList.add("z-1")
          }, 100)
        })
      })
* {
        margin: 0;
        padding: 0;
        box-sizing: border-box;
      }

      .gallery-container {
        position: relative;
        width: 100%;
        height: 90vh;
        background-color: yellow;
        overflow: hidden;
      }

      .scrollable-area {
        width: 100%;
        height: 100%;
        overflow-x: auto;
      }

      .gallery-items {
        display: flex;
        min-width: 100%;
        height: 100%;
      }

      .gallery-item {
        flex: 0 0 auto;
        height: 100%;
        display: flex;
      }

      .gallery-item img {
        max-width: 100%;
        height: auto;
        object-fit: contain;
      }

      .gallery-item iframe {
        background-color: blue;
        width: auto;
        width: 800px;
      }

      .relative {
        position: relative;
      }

      .background {
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
      }

      .z-1 {
        z-index: 1;
      }

      .-z-1 {
        z-index: -1;
      }
 <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>

    <div class="gallery-container inactive">
      <div class="scrollable-area">
        <div class="gallery-items">
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG"
            />
          </div>
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/d/da/Sky_landscape.jpg"
            />
          </div>
          <div class="gallery-item relative">
            <iframe
              src="https://player.vimeo.com/video/584985260"
              frameborder="0"
              allow="fullscreen"
            ></iframe>
            <div class="background z-1"></div>
          </div>
          <div class="gallery-item">
            <img
              src="https://upload.wikimedia.org/wikipedia/commons/1/16/Appearance_of_sky_for_weather_forecast%2C_Dhaka%2C_Bangladesh.JPG"
            />
          </div>
        </div>
      </div>
    </div>

Answer №4

In the past, I've utilized a somewhat intricate method. This involves injecting an event listener into the iframe and then returning back to the parent page.

Upon quickly testing it with your example, it seems to work to some extent.

$(document).ready(function() {
   
   ....insert your code here.....
   
   ...and include this snippet...
    //this will inject event listeners into any iframes on your page; you can customize the selector as needed
    let iframes = document.getElementsByTagName('iframe')
    for(let iframe of iframes){
        iframe.contentWindow.addEventListener("wheel", function(e){

           //IMPORTANT: you need to call a function outside this scope, as this is being executed inside the iframe, not here. We are essentially instructing the iframe to trigger a function "someFunction()" within its parent (this window).

            window.parent.someFunction(e) //someFunction() is defined below
            
        })
    }
 
    
});

// this function must exist, or else a script error will occur
  function someFunction(e){
    //the wheel event should be re-broadcasted here; thank you, Mr. iFrame :0)

      console.log(e) //on my end, no action is necessary; the gallery continues scrolling automatically, or you may need to pass this event to your main scrolling code

  }

However, keep in mind that this method may not function correctly if the page loaded in the iframe enforces strict CORS policies (your video example appears to work seamlessly).

Additionally, if the iframe's background is visible, it may not respond to the wheel event. In such cases, attaching another onwheel event directly to the iframe might resolve the issue.

If this explanation doesn't suffice, well, at least you've learned a bit about working with iframes.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

ObjectArray in Node.js

Building an object array in my node app involves transforming another object array. Let's assume the initial object array is structured like this... levels: [ { country_id: 356, country_name: "aaa", level0: "bbbb", level1: "cccc", level2: "dddd", le ...

Using jQuery to encapsulate HTML within a div tag

I am looking to insert some HTML markup into my webpage using jQuery. I want to wrap the highlighted yellow code below with <div class="section">...</div>. Here is an example of what I need: <div class="section"> <div>...< ...

At what point does the promise's then function transition to being either fulfilled or rejected?

When dealing with promises in JavaScript, the then() method returns a promise that can be in one of three states: pending, fulfilled, or rejected. You can create a promise using the resolved and rejected methods to indicate when it should be fulfilled or r ...

Jssor Slider: Captions briefly overlap just as the slideshow begins to play

I am currently working on a slideshow with 3 caption-only slides. Here is the code snippet: <div id="sliderHeader_container" style="position: relative; width: 965px; height: 85px;"> <!-- Slides Container --> <div u="slides" style=" ...

What is the best way to assign different colors to rows in a SharePoint list on each individual page?

After following the instructions provided on this website, <script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.10.0/jquery.min.js"> </script> <script type="text/javascript"> $(document).ready(function () { ...

Creating a jQuery Mobile Multi-Select feature with dynamically loaded data

I'm facing an issue with integrating jQuery Mobile and AngularJS. I need a multi-select option for users to choose categories, which have parent-child relationships. The data structure is fetched asynchronously from a database. The problem is that t ...

What is the best way to track and document the specific Context Provider being utilized while invoking useContext?

After creating a custom component that displays AuthContext.Provider with specific values, I encountered an issue. Even though it functions correctly, when I utilize useContext within a child of the AuthProvider component, my code editor (VS Code) is unabl ...

A clever method for implementing lazy-loading using mobx

Can you provide guidance on the most effective way to lazy load properties with MobX? I've been grappling with this issue for a few days now, and I've struggled to find suitable examples ever since strict mode was introduced. While I appreciate ...

Encountered SyntaxError: An unexpected token has been found while integrating leaflet with flask

Despite adding all necessary scripts and configuring my API_KEY in config.js, I keep getting an error message saying "Uncaught SyntaxError: Unexpected token." I have double-checked my API key multiple times, and it seems to be correct. Here is a snippet f ...

Experiencing a problem with the localhost connection

I've been trying to work on this issue using React and local host, but it keeps showing the direct file instead of the website. I've been stuck on this problem for the past 3-4 hours and still haven't been able to find a solution. I'm h ...

Having trouble getting the Bootstrap 5 Modal to function properly within an Electron app

Facing an issue with my web app using Bootstrap 5, as the modal is not displaying properly. Below is the HTML code for the modal and the button that triggers it: <div class="modal" tabindex="-1" id=&quo ...

Is it possible to duplicate the mesh thousands of times and animate it without causing significant performance issues

I have created a demo with numerous cubes that share the same geometry and texture: texture = THREE.ImageUtils.loadTexture ... material = new THREE.MeshLambertMaterial( map: texture ) geometry = new THREE.BoxGeometry( 1, 1, 1 ) cubes = [] for i in [0..1 ...

Unable to transfer Vue.js components in and out of the project

I have a directory structured like this. VueTree components Classic.vue Modern.vue index.js The Classic and Modern components consist of a template, export default {}, and a style. I am importing both of them in index.js as: import Classic ...

Reduced complications with mixin scopes

I have developed a parametric mixin that utilizes a unique "node-value" system to locate the children of an element. The process involves detecting the children through a loop function named ".extractArrays", which contains ".deliverChild" within it. Subse ...

Can you explain the distinction between String[] and [String] in TypeScript?

Can you explain the distinction between String[] and [String] in typescript? Which option would be more advantageous to use? ...

What is the best way to format or delete text enclosed in quotation marks within an anchor tag using CSS or JavaScript?

I have encountered an issue with a dynamically generated login form. When I select the 'Forgot Password' option, a new 'Back to Login' message appears along with a separating '|' line. Removing this line is proving challenging ...

Resizing the elements within an iframe: Troubleshooting problems with embedding Bandcamp players

My goal is to embed a Bandcamp music player on my WordPress blog page, but I'm facing a challenge. The maximum width of the current player is 700px and I need it to be 900px. After consulting with someone at Bandcamp, they directed me to the old versi ...

What is the typical approach of JavaScript frameworks towards the 304 not-modified response?

Wondering about the inner workings of Jquery and web development in general. When a JavaScript file is requested and the server sends a 304 not-modified response, how does a framework handle it: Does it load the JS file from cache and run it? Or does it ...

Exploring Twig variables in Node.js with the node-twig package

Despite following the documentation meticulously, and experimenting with various methods, I am still unable to achieve success. I have attempted using the code snippet below, trying to reference the variable in the main file like this: // None of the opti ...

Simple method for converting pixel values to em units

I'm searching for a simple method to incorporate a line of code into one of my plugins in order to convert specific pixel values into em values. My project's layout requires the use of ems, and I'd prefer not to rely on external plugins for ...