IntersectionObserver 在 Safari 或 iOS 中不工作
IntersectionObserver not working in Safari or iOS
我有一段代码根据元素是从顶部还是底部滚入或滚出视口,向元素添加不同的 css class。
它使用 Intersection Observer 因为它应该比 scroll
事件更好地处理大量元素。
但是,我在使用这段代码时遇到了两个问题:
- 它在 Safari(最新版本)中不起作用
- 它不适用于 Apple 移动设备
这很奇怪,因为 IntersectionObserver 应该可以在 Safari 甚至 iOS 上的移动浏览器上正常工作。
您可以find the code on jsFiddle或在此处查看代码段:
const config = {
// Add root here so rootBounds in entry object is not null
root: document,
// Margin to when element should take action
rootMargin: '-50px 0px',
// Callback will be fired 30 times during intersection
threshold: [...Array(30).keys()].map(x => x / 29)
};
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach((entry, index) => {
const element = entry.target;
// Get root element (document) coords
const rootTop = entry.rootBounds.top;
const rootBottom = entry.rootBounds.height;
// Get div coords
const topBound = entry.boundingClientRect.top - 50; // margin in config
const bottomBound = entry.boundingClientRect.bottom;
let className;
// Do calculations to get class names
if (topBound < rootTop && bottomBound < rootTop) {
className = "outview-top";
} else if (topBound > rootBottom) {
className = "outview-bottom";
} else if (topBound < rootBottom && bottomBound > rootBottom) {
className = "inview-bottom";
} else if (topBound < rootTop && bottomBound > rootTop) {
className = "inview-top";
}
element.setAttribute('data-view', className);
});
}, config);
const viewbox = document.querySelectorAll('.viewme');
viewbox.forEach(image => {
observer.observe(image);
});
body {
text-align: center;
}
.margins {
position: fixed;
top: 50px;
bottom: 50px;
border-top: 2px dashed;
border-bottom: 2px dashed;
z-index: 1;
left: 0;
width: 100%;
pointer-events: none;
}
.hi {
padding: 40vh 0;
background: lightgray;
}
.box {
width: 23%;
min-width: 100px;
height: 40vh;
margin-bottom: 10px;
background: lightblue;
display: inline-block;
}
.viewme {
transition: all .3s ease;
}
.viewme[data-view='inview-top'],
.viewme[data-view='inview-bottom'] {
opacity: 1;
transform: translateY(0);
}
.viewme[data-view='outview-top'] {
opacity: 0;
transform: translateY(-20px);
}
.viewme[data-view='outview-bottom'] {
opacity: 0;
transform: translateY(20px);
}
<p class="hi">Scroll down and back up</p>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class='margins'>
</div>
到目前为止,关于可能导致这些问题的原因,我有两个提示:
- 在 Safari 开发者控制台中,它说在我的 JS 代码中第 10 行和第 38 行之间有一个
Type error
- 我注意到其他定义
root: document
的脚本不适用于 iOS。相反,它们在定义 root: null
时起作用。但是,由于rootBounds
,我无法使用root: null
。我试图将我的 html 包装在 div 中并将 div 的 id 设置为根元素,但这没有用(see here)。
非常感谢任何解决这两个问题的帮助。不过,请注意,上面的代码不是我写的,也不是很懂。
虽然我不能指出错误的确切原因,但我确实有一个解决方案:
尝试使用 document.body
作为 root
并为 html
和 body
定义大小和滚动行为。
我认为这与 document
不仅仅是一个简单的 html 节点(我也尝试使用 document.documentElement
但没有成功)以及 Safari 如何为其初始化盒模型有关。
无论如何,这是 fiddle https://jsfiddle.net/gion_13/okrcgejt/8/ 和 iOS 和 Mac Safari 上测试的更新工作截屏:
我最近遇到了同样的问题,为了解决这个问题我不得不
设置根:空
我找不到解决您问题的方法,但是当我将阈值从 1 降低到 0.9 时,我的问题得到了解决。当它设置为 1 时,路口观察器在 Safari 中不工作。但它在 Firefox 和 Chrome.
中完美运行
const options={
root:null,
rootMargin:'0px',
threshold:0.9
};
无论根设置为 null 还是 document.body 都有效。出于某种原因,当我要求它仅在对象 100% 完全可见时触发时,Safari 中的 Intersection Observer 不起作用,但它适用于 90%。我希望这可以帮助那里的人。
对我来说 document.body
和任何不同于 [] || 0
的阈值。
const options = {
root: document.body,
threshold: 0.000001
}
我想我已经找到问题所在了。如果您的项目对于视口(屏幕)而言太高(高度),则该元素永远不会适合阈值,至少它足够小。这就是为什么我认为@donquixote 的答案很奇怪但正确的原因。
const options = {
root: null,
rootMargin: "0px",
threshold: 0.1,
};
这对我有用。
仅供参考,如果您查看 MDN - https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#browser_compatibility
root 不能是 Safari、IE、Safari IOS 和 Firefox Android 中的文档。至于这是为什么,我不知道,只是分享一下,这已经是一个已知问题。
我有一段代码根据元素是从顶部还是底部滚入或滚出视口,向元素添加不同的 css class。
它使用 Intersection Observer 因为它应该比 scroll
事件更好地处理大量元素。
但是,我在使用这段代码时遇到了两个问题:
- 它在 Safari(最新版本)中不起作用
- 它不适用于 Apple 移动设备
这很奇怪,因为 IntersectionObserver 应该可以在 Safari 甚至 iOS 上的移动浏览器上正常工作。
您可以find the code on jsFiddle或在此处查看代码段:
const config = {
// Add root here so rootBounds in entry object is not null
root: document,
// Margin to when element should take action
rootMargin: '-50px 0px',
// Callback will be fired 30 times during intersection
threshold: [...Array(30).keys()].map(x => x / 29)
};
let observer = new IntersectionObserver(function(entries, observer) {
entries.forEach((entry, index) => {
const element = entry.target;
// Get root element (document) coords
const rootTop = entry.rootBounds.top;
const rootBottom = entry.rootBounds.height;
// Get div coords
const topBound = entry.boundingClientRect.top - 50; // margin in config
const bottomBound = entry.boundingClientRect.bottom;
let className;
// Do calculations to get class names
if (topBound < rootTop && bottomBound < rootTop) {
className = "outview-top";
} else if (topBound > rootBottom) {
className = "outview-bottom";
} else if (topBound < rootBottom && bottomBound > rootBottom) {
className = "inview-bottom";
} else if (topBound < rootTop && bottomBound > rootTop) {
className = "inview-top";
}
element.setAttribute('data-view', className);
});
}, config);
const viewbox = document.querySelectorAll('.viewme');
viewbox.forEach(image => {
observer.observe(image);
});
body {
text-align: center;
}
.margins {
position: fixed;
top: 50px;
bottom: 50px;
border-top: 2px dashed;
border-bottom: 2px dashed;
z-index: 1;
left: 0;
width: 100%;
pointer-events: none;
}
.hi {
padding: 40vh 0;
background: lightgray;
}
.box {
width: 23%;
min-width: 100px;
height: 40vh;
margin-bottom: 10px;
background: lightblue;
display: inline-block;
}
.viewme {
transition: all .3s ease;
}
.viewme[data-view='inview-top'],
.viewme[data-view='inview-bottom'] {
opacity: 1;
transform: translateY(0);
}
.viewme[data-view='outview-top'] {
opacity: 0;
transform: translateY(-20px);
}
.viewme[data-view='outview-bottom'] {
opacity: 0;
transform: translateY(20px);
}
<p class="hi">Scroll down and back up</p>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class="box viewme"></div>
<div class='margins'>
</div>
到目前为止,关于可能导致这些问题的原因,我有两个提示:
- 在 Safari 开发者控制台中,它说在我的 JS 代码中第 10 行和第 38 行之间有一个
Type error
- 我注意到其他定义
root: document
的脚本不适用于 iOS。相反,它们在定义root: null
时起作用。但是,由于rootBounds
,我无法使用root: null
。我试图将我的 html 包装在 div 中并将 div 的 id 设置为根元素,但这没有用(see here)。
非常感谢任何解决这两个问题的帮助。不过,请注意,上面的代码不是我写的,也不是很懂。
虽然我不能指出错误的确切原因,但我确实有一个解决方案:
尝试使用 document.body
作为 root
并为 html
和 body
定义大小和滚动行为。
我认为这与 document
不仅仅是一个简单的 html 节点(我也尝试使用 document.documentElement
但没有成功)以及 Safari 如何为其初始化盒模型有关。
无论如何,这是 fiddle https://jsfiddle.net/gion_13/okrcgejt/8/ 和 iOS 和 Mac Safari 上测试的更新工作截屏:
我最近遇到了同样的问题,为了解决这个问题我不得不
设置根:空
我找不到解决您问题的方法,但是当我将阈值从 1 降低到 0.9 时,我的问题得到了解决。当它设置为 1 时,路口观察器在 Safari 中不工作。但它在 Firefox 和 Chrome.
中完美运行const options={
root:null,
rootMargin:'0px',
threshold:0.9
};
无论根设置为 null 还是 document.body 都有效。出于某种原因,当我要求它仅在对象 100% 完全可见时触发时,Safari 中的 Intersection Observer 不起作用,但它适用于 90%。我希望这可以帮助那里的人。
对我来说 document.body
和任何不同于 [] || 0
的阈值。
const options = {
root: document.body,
threshold: 0.000001
}
我想我已经找到问题所在了。如果您的项目对于视口(屏幕)而言太高(高度),则该元素永远不会适合阈值,至少它足够小。这就是为什么我认为@donquixote 的答案很奇怪但正确的原因。
const options = {
root: null,
rootMargin: "0px",
threshold: 0.1,
};
这对我有用。
仅供参考,如果您查看 MDN - https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API#browser_compatibility
root 不能是 Safari、IE、Safari IOS 和 Firefox Android 中的文档。至于这是为什么,我不知道,只是分享一下,这已经是一个已知问题。