英文:
How can I make an iframe behave like other media elements in a horizontal scrolling area?
问题
这是我的代码:
<!-- 开始代码片段: js 隐藏: false 控制台: true Babel: false -->
<!-- 语言: lang-js -->
$(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);
}
});
});
<!-- 语言: lang-css -->
* {
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;
}
<!-- 语言: lang-html -->
<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>
<!-- 结束代码片段 -->
如果您在媒体库上并正常向下滚动,您将首先在库中水平滚动。这是有意的。但是,当包含iframe时,它会停止。我已经尝试在滚动期间阻止指针事件,但这会导致一些其他问题,比如无法单击视频的播放按钮。
此外,找到一种始终将iframe的大小调整为视频的大小并适应水平库的整体高度的方法将会很酷。有人知道这是如何工作的吗?
英文:
This is my code:
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
$(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);
}
});
});
<!-- language: lang-css -->
* {
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
If you have the cursor on the media gallery and scroll down normally, you will first scroll horizontally through the gallery. That's on purpose. However, it stops when an iframe is included. I've already tried to prevent the pointer events during scrolling, but it leads to some other issues like making it impossible to click the play button of a video.
Also, it would be cool to find a way to always resize the iframe to that of the video and to fit the overall height of the horizontal gallery. Does anyone know how this works?
答案1
得分: 2
你可以向滚动的 div 添加一个叠加层,以禁用 iframe 的指针事件,并在鼠标移动时重新启用指针事件。
基于trincot的答案“如何检测鼠标移动已停止”,我们可以使用自定义的 mousestop
事件。
- 通过附加一个叠加层,我们禁用了 iframe 的指针事件。
- 根据鼠标移动来切换/丢弃叠加层:
如果鼠标移动已停止(使用 setTimeout 延迟),我们添加/激活叠加层以阻止 iframe 内容中的指针事件 - 启用滚轮滚动事件。
当鼠标移动到滑块包装上时,我们可以访问 iframe/幻灯片按钮。 - iframe 的尺寸通过
aspect-ratio
简单设置,例如 16/9
我添加了一个半透明的红色色调来说明叠加层的切换。
英文:
You might add an overlay to your scrolling div to disable iframe pointer events and re-enable pointer events on mouse movements.
Based on trincot's answer "How to detect when mousemove has stopped"
we could use a custom mousestop
event.
- we disable iframe pointer events by appending an overlay.
- we're toggling/discarding the overlay according to mouse movements:
if mouse moves were stopped (using a setTimeout delay) we add/activate the overlay to block pointer events within the iframe content – enabling wheel scroll events
When moving over the slider wrap – we can access iframe/slide buttons. - iframe dimensions are simply set via
aspect-ratio
e.g. 16/9
<!-- begin snippet: js hide: false console: false babel: false -->
<!-- language: lang-js -->
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);
}
});
});
<!-- language: lang-css -->
* {
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;
}
<!-- language: lang-html -->
<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/big-buck-bunny-1080p@0.0.6/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>
<!-- end snippet -->
I've added a semitransparent reddish tint to illustrate the overlay toggling.
答案2
得分: 0
修改你的jQuery代码以处理图片和iframe的滚动事件,你还可以动态调整iframe的大小以适应其内容,并相应调整水平图库的高度。
<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);
}
});
// 调整iframe大小以适应内容
function resizeIframes() {
$('.gallery-item iframe').each(function() {
this.height = this.contentWindow.document.body.scrollHeight + 'px';
});
}
// 在初始加载和窗口调整时调用该函数
resizeIframes();
$(window).resize(function() {
resizeIframes();
});
});
</script>
英文:
Modify your jQuery code to handle scrolling events for both images and iframes and you may also dynamically resize the iframes to fit their content and adjust 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';
});
}
// Call the function on initial load and on window resize
resizeIframes();
$(window).resize(function() {
resizeIframes();
});
});
</script>
答案3
得分: 0
我认为你可以使用 setTimeout
来实现这个。我知道这不是一个好的方法。如果你可以更新 iframe
的源网站,那就没问题。
$(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>
英文:
I think you can use setTimeout
to implement this. I know this is not a good way. If you can update the iframe
source website, no problem.
<!-- begin snippet: js hide: false console: true babel: false -->
<!-- language: lang-js -->
$(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)
})
})
<!-- language: lang-css -->
* {
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;
}
<!-- language: lang-html -->
<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>
<!-- end snippet -->
答案4
得分: 0
以下是翻译好的部分:
有一种复杂的方法,我过去曾经使用过。它涉及将事件监听器注入到iFrame中,然后返回到父页面。
我刚刚进行了一个非常快速的测试,它在您的示例中有点效果。
$(document).ready(function() {
....在这里添加您的代码.....
...然后添加这个...
//这将向您页面中的任何iframe注入事件监听器,您可以以任何方式选择选择器,这只是一个示例
let iframes = document.getElementsByTagName('iframe')
for(let iframe of iframes){
iframe.contentWindow.addEventListener("wheel", function(e){
//重要提示:您需要调用此作用域外部的函数,因为实际上是在iframe内部执行,而不是在这里执行。我们基本上是告诉iframe触发其父级中的函数"someFunction()",这是我们所在的窗口。
window.parent.someFunction(e) //someFunction()在下面
})
}
});
//否则将会出现脚本错误
function someFunction(e){
//我们应该在这里重新广播鼠标滚轮事件,谢谢iFrame先生:0)
console.log(e) //在我的计算机上不需要对此做任何处理,图库会自行滚动,或者您可能需要将此事件传递给主滚动代码
}
现在,如果您加载到iFrame中的页面实现了严格的CORS,这很可能不起作用(您的视频示例不挑剔,似乎运行正常)。
此外,如果iFrame的背景暴露出来,它可能不会对滚轮事件做出反应,但这可能可以通过将另一个onwheel事件附加到iFrame本身来解决。
希望这有所帮助,如果没有,那么这只是有关iFrame的一些小知识。
英文:
There is one convoluted way that I've used in the past. It's an event listener injection into the iFrame and then a return back to the parent page.
I've just done a very quick test and it kinda works with your example.
$(document).ready(function() {
....your code here.....
...and just add this...
//this will inject event listeners to any iframe in your page, you can do the selector any way you want, this is just an example
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 actually being executed inside the iframe, not here. We are basically telling the iframe to fire a function "someFunction()" inside it's parent which is this window where we are.
window.parent.someFunction(e) //someFunction() is further below
})
}
});
// this needs to exists otherwise you will get a script error
function someFunction(e){
//and we should get the wheel event re-broadcast here, thank you mister iFrame :0)
console.log(e) // on my comp don't need to do anything with this, the gallery keeps scrolling on its own or you may need to just pass this event to your main scrolling code
}
Now, this will most likely not work if the page that you are loading in the iFrame implements strict CORS (your video example is not fussy and seems to work fine)
Also, it may require some tweaking if the iFrame background is exposed it may not to react to the wheel but that may be solved by attaching another onwheel event to the iframe itself.
Hope that helps, if not, heck it's a bit of iFrame trivia.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论