元素何时可见的事件侦听器?

我正在构建一个工具栏,它将被包含到一个页面中。将包含它的 div 将默认为 显示: 无。有没有一种方法,我可以把一个事件监听器在我的工具栏,当它变得可见,以便它可以初始化侦听?或者我必须从包含页面传递一个变量给它?

谢谢

215430 次浏览

至少有一种方法,但不是很好。你可以像下面这样轮询元素:

var previous_style,
poll = window.setInterval(function()
{
var current_style = document.getElementById('target').style.display;
if (previous_style != current_style) {
alert('style changed');
window.clearInterval(poll);
} else {
previous_style = current_style;
}
}, 100);

DOM 标准也指定了 突变事件,但是我从来没有机会使用它们,而且我不确定它们是否得到了很好的支持。你可以这样使用它们:

target.addEventListener('DOMAttrModified', function()
{
if (e.attrName == 'style') {
alert('style changed');
}
}, false);

这个代码我一下子就想到了,所以我不确定它能不能用。

最好的和最简单的解决方案是在显示目标的函数中有一个回调。

Javascript 事件处理 用户交互,如果你的代码组织得足够好,你应该能够在可见性改变的同一地方调用初始化函数(也就是说,你不应该在很多地方改变 myElement.style.display,相反,调用一个函数/方法来做这件事或者其他你可能想要的事情)。

我的解决办法是:

; (function ($) {
$.each([ "toggle", "show", "hide" ], function( i, name ) {
var cssFn = $.fn[ name ];
$.fn[ name ] = function( speed, easing, callback ) {
if(speed == null || typeof speed === "boolean"){
var ret=cssFn.apply( this, arguments )
$.fn.triggerVisibleEvent.apply(this,arguments)
return ret
}else{
var that=this
var new_callback=function(){
callback.call(this)
$.fn.triggerVisibleEvent.apply(that,arguments)
}
var ret=this.animate( genFx( name, true ), speed, easing, new_callback )
return ret
}
};
});


$.fn.triggerVisibleEvent=function(){
this.each(function(){
if($(this).is(':visible')){
$(this).trigger('visible')
$(this).find('[data-trigger-visible-event]').triggerVisibleEvent()
}
})
}
})(jQuery);

例如:

if(!$info_center.is(':visible')){
$info_center.attr('data-trigger-visible-event','true').one('visible',processMoreLessButton)
}else{
processMoreLessButton()
}


function processMoreLessButton(){
//some logic
}

正如@fight a 所说的,如果这是你自己的网页,在可见元素之后,应该只运行需要运行的任何内容。

但是,为了回答这个问题(以及任何 制作 Chrome 或 Firefox 扩展,其中这是一个常见的用例) ,变异摘要变异观察者将允许 DOM 更改触发事件。

例如,为添加到 DOM 中的具有 data-widget属性的元素触发一个事件:

var observer = new MutationObserver(function(mutations) {
// For the sake of...observation...let's output the mutation to console to see how this all works
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});


// Notify me of everything!
var observerConfig = {
attributes: true,
childList: true,
characterData: true
};


// Node, config
// In this case we'll listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);

反应包括 ABC0,ABC1,valueChanged等等valueChanged包括所有属性,包括 display等。

只是对 DOMAttrAmendment 事件监听器浏览器支持进行评论:

跨浏览器支持

这些事件没有在不同的浏览器上一致地实现,例如:

  • 在版本9之前的 IE 根本不支持变异事件 在版本9中没有正确地实现其中的一些功能(例如, (DOMNodeInserted)

  • WebKit 不支持 DOMAttrAmendment (参见 WebKit bug 8191和 解决方案)

  • “突变名称事件”,即 DOMElementNameChanged 和 Firefox 不支持 DOMAttributeNameChanged (截至版本) 也许在其他浏览器中也是如此

资料来源: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

我也遇到过同样的问题,所以我为我们的网站创建了一个 jQuery 插件来解决这个问题。

Https://github.com/shaunbowe/jquery.visibilitychanged

下面是你将如何使用它基于你的例子:

$('#contentDiv').visibilityChanged(function(element, visible) {
alert("do something");
});

展望未来,新的 HTML 交叉观察者 API 就是您要寻找的东西。它允许您配置一个回调,每当一个称为目标的元素与设备视口或指定的元素相交时,就会调用该回调。它可以在最新版本的 Chrome,Firefox 和 Edge 中使用。有关更多信息,请参见 https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

观察显示的简单代码示例: 无切换:

// Start observing visbility of element. On change, the
//   the callback is called with Boolean visibility as
//   argument:


function respondToVisibility(element, callback) {
var options = {
root: document.documentElement,
};


var observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
callback(entry.intersectionRatio > 0);
});
}, options);


observer.observe(element);
}

行动: https://jsfiddle.net/elmarj/u35tez5n/5/

var targetNode = document.getElementById('elementId');
var observer = new MutationObserver(function(){
if(targetNode.style.display != 'none'){
// doSomething
}
});
observer.observe(targetNode, { attributes: true, childList: true });

我可能会晚一点,但是您可以使用 MutationObserver 来观察所需元素的任何更改。如果发生任何更改,您只需检查元素是否显示。

如果您只是想在视图中某个元素变得可见时运行一些代码:

function onVisible(element, callback) {
new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if(entry.intersectionRatio > 0) {
callback(element);
observer.disconnect();
}
});
}).observe(element);
}

当元素变得可见时(稍微有点事件) ,交集观察者调用 callback,然后用 .disconnect()销毁自己。

像这样使用它:

onVisible(document.querySelector("#myElement"), () => console.log("it's visible"));

如果希望在元素变成 完全可见时触发回调,那么应该将 entry.intersectionRatio > 0更改为 entry.intersectionRatio === 1

扩展 Elmar 早期的答案,我使用这个来将焦点放在 Bootstrap 导航栏子菜单中的一个输入框上。

当菜单被展开时,我想把焦点放在搜索框上。.Onfocus ()不工作,我认为是因为在触发事件时元素不可见(即使使用 mouseup 事件)。不过,这种做法非常奏效:

<ul class="navbar-nav ms-auto me-0 ps-3 ps-md-0">
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" title="Search" id="navbardrop" data-bs-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
<i class="fas fa-search"></i>
</a>
<div class="dropdown-menu dropdown-menu-end search-menu">
<form action="{% url 'search' %}" method="get">
<div class="form-group row g-1 my-1 pb-1">
<div class="col">
<input type="text" name="query" id="searchbox" class="form-control py-1 ps-2" value="{% if search_query %}\{\{ search_query }}{% endif %}">
</div>
<div class="col-auto">
<input type="submit" value="Search" class="btn-primary form-control py-1">
</div>
</div>
</form>
</div>
</li>
</ul>

然后是 js:

respondToVisibility = function (element, callback) {
var options = {
root: document.documentElement,
};


var observer = new IntersectionObserver((entries, observer) => {
entries.forEach((entry) => {
callback(entry.intersectionRatio > 0);
});
}, options);


observer.observe(element);
};


respondToVisibility(document.getElementById("searchbox"), (visible) => {
if (visible) {
document.getElementById("searchbox").focus();
}
});

一个简单的解决方案是使用 ResizeObserver,它甚至可以用于嵌套元素。

它应该可以在所有现代浏览器(https://developer.mozilla.org/en-US/docs/Web/API/Resize_Observer_API)中工作

当一个元素有 css 规则 display:none应用到它(无论是直接或通过祖先元素) ,那么它的所有维度将为零。所以为了检测可见度,我们只需要一个非零维度的元素。

const block=document.querySelector("#the-block")
const resizewatcher=new ResizeObserver(entries => {
for (const entry of entries){
console.log("Element",entry.target,
(entry.contentRect.width == 0) ?
"is now hidden" :
"is now visible"
)
}
})
resizewatcher.observe(block)