让 div 可见时触发操作的 jQuery 事件

Posted

技术标签:

【中文标题】让 div 可见时触发操作的 jQuery 事件【英文标题】:jQuery event to trigger action when a div is made visible 【发布时间】:2010-11-16 13:00:28 【问题描述】:

我在我的网站中使用 jQuery,我想在某个 div 可见时触发某些操作。

是否可以将某种“不可见”事件处理程序附加到任意 div 并在 div 可见时运行某些代码?

我想要类似下面的伪代码:

$(function() 
  $('#contentDiv').isvisible(function() 
    alert("do something");
  );
);

在 contentDiv 真正可见之前,不应触发 alert("do something") 代码。

谢谢。

【问题讨论】:

见这里:***.com/questions/1462138/… 这能回答你的问题吗? How to check if element is visible after scrolling? 【参考方案1】:

您始终可以添加到原始的 .show() 方法,这样您就不必在每次显示某些内容或需要它与旧代码一起使用时触发事件:

jQuery 扩展:

jQuery(function($) 

  var _oldShow = $.fn.show;

  $.fn.show = function(speed, oldCallback) 
    return $(this).each(function() 
      var obj         = $(this),
          newCallback = function() 
            if ($.isFunction(oldCallback)) 
              oldCallback.apply(obj);
            
            obj.trigger('afterShow');
          ;

      // you can trigger a before show if you want
      obj.trigger('beforeShow');

      // now use the old function to show the element passing the new callback
      _oldShow.apply(obj, [speed, newCallback]);
    );
  
);

使用示例:

jQuery(function($) 
  $('#test')
    .bind('beforeShow', function() 
      alert('beforeShow');
    ) 
    .bind('afterShow', function() 
      alert('afterShow');
    )
    .show(1000, function() 
      alert('in show callback');
    )
    .show();
);

这有效地让您在执行原始 .show() 方法的正常行为的同时执行一些 beforeShow 和 afterShow。

你也可以创建另一个方法,这样你就不必重写原来的 .show() 方法了。

【讨论】:

编辑:这种方法只有一个缺点:您必须对所有显示元素的方法重复相同的“扩展”:show()、slideDown() 等。更通用的是需要一劳永逸地解决这个问题,因为委托()或直播()不可能有“就绪”事件。 好的,唯一的问题是fadeTo函数实现这个功能后不能正常工作 您的代码似乎不适用于最新的 jQuery(此评论日期为 1.7.1)。我已经稍微修改了这个解决方案以使用最新的 jQuery:***.com/a/9422207/135968 无法让该代码与 ajax 响应触发的 div 可见性一起工作。 这个看起来比@mkmurray 快:***.com/a/30678695/1145224【参考方案2】:

DOM mutation observers 正在解决此问题。它们允许您将观察者(一个函数)绑定到更改 dom 元素的内容、文本或属性的事件。

随着IE11的发布,各大浏览器都支持该功能,查看http://caniuse.com/mutationobserver

示例代码如下:

$(function() 
  $('#show').click(function() 
    $('#testdiv').show();
  );

  var observer = new MutationObserver(function(mutations) 
    alert('Attributes changed!');
  );
  var target = document.querySelector('#testdiv');
  observer.observe(target, 
    attributes: true
  );

);
<div id="testdiv" style="display:none;">hidden</div>
<button id="show">Show hidden div</button>

<script type="text/javascript" src="https://code.jquery.com/jquery-1.9.1.min.js"></script>

【讨论】:

可惜IE还不支持。 caniuse.com/mutationobserver -> 查看支持它的浏览器。 这确实有效,而且我不需要支持旧版浏览器,所以非常完美!我在这里添加了答案的 JSFiddle 证明:jsfiddle.net/DanAtkinson/26URF 在 chrome 中运行良好,但在 blackberry 10 cascade webview 中无法运行(如果有人关心的话;)) 如果可见性变化是由被监控的祖先的属性变化引起的,这似乎不起作用。 这似乎在 Chrome 51 中不起作用,不知道为什么。运行上面的代码并按下按钮,没有警报。【参考方案3】:

没有可以挂钩的本机事件,但是在使用 .trigger 函数使 div 可见后,您可以从脚本中触发事件

例如

//declare event to run when div is visible
function isVisible()
   //do something



//hookup the event
$('#someDivId').bind('isVisible', isVisible);

//show div and trigger custom event in callback when div is visible
$('#someDivId').show('slow', function()
    $(this).trigger('isVisible');
);

【讨论】:

我的限制是我不一定有权访问 show() 是我的 div 的代码。所以我将无法真正调用 trigger() 方法。 JS 是由我所在组织之外的开发团队提供的。它也是一个“黑匣子”,所以如果可能的话,我们不想修改那个代码。不过,这可能是我们唯一的选择。 你总是可以用你自己的实现来覆盖他们的 js 函数。然而听起来很可怕! @redsquare: 如果show() 被从上面讨论的代码块以外的多个地方调用怎么办? 对于这个例子,你应该把函数名改成onIsVisible,因为现在“isVisible”的使用有点模糊。【参考方案4】:

你可以使用 jQuery 的Live Query plugin。 并编写代码如下:

$('#contentDiv:visible').livequery(function() 
    alert("do something");
);

那么每当 contentDiv 可见时,就会提醒“做某事”!

【讨论】:

好吧,那行得通。我曾考虑过并拒绝了它,因为它不太可能起作用,没有尝试过。应该试过了。 :) 对我不起作用。我收到错误“livequery 不是函数”。尝试使用“jquery-1.12.4.min.js”和“jquery-3.1.1.min.js” @Paul:这是一个插件 这可能会大大降低您的网站速度! livequery 插件对属性执行快速轮询,而不是使用现代有效的方法,例如 DOM 变异观察者。所以我更喜欢@hegemon 的解决方案:***.com/a/16462443/19163(并且只使用轮询作为旧 IE 版本的后备) – vog 1 小时前【参考方案5】:

redsquare's solution 是正确答案。

但作为 IN-THEORY 解决方案,您可以编写一个函数来选择由 .visibilityCheck 分类的元素(并非所有可见元素)并检查它们的 @987654323 @ 适当的价值;如果true 然后做点什么。

之后,应使用setInterval() 函数定期执行该函数。您可以在成功调用后使用clearInterval() 停止计时器。

这是一个例子:

function foo() 
    $('.visibilityCheck').each(function() 
        if ($(this).is(':visible'))
            // do something
        
    );


window.setInterval(foo, 100);

您还可以对其进行一些性能改进,但是,在实际中使用该解决方案基本上是荒谬的。所以...

【讨论】:

在 setTimeout/setInterval 中使用隐含函数的形式不好。使用 setTimeout(foo, 100) 我必须把它交给你,这很有创意,虽然很讨厌。它又快又脏,足以以符合 IE8 的方式为我解决问题。谢谢! display:none?【参考方案6】:

以下代码(取自http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/)将使您能够使用$('#someDiv').on('show', someFunc);

(function ($) 
  $.each(['show', 'hide'], function (i, ev) 
    var el = $.fn[ev];
    $.fn[ev] = function () 
      this.trigger(ev);
      return el.apply(this, arguments);
    ;
  );
)(jQuery);

【讨论】:

这对我来说非常有效,但重要的是要注意该函数破坏了显示和隐藏函数的链接,这破坏了很多插件。在el.apply(this, arguments) 前面添加一个返回来解决这个问题。 这就是我要找的!需要在@jaimerump 的评论中添加返回【参考方案7】:

如果您想通过 $.show、toggle、toggleClass、addClass 或 removeClass 在实际可见的所有元素(和子元素)上触发事件:

$.each(["show", "toggle", "toggleClass", "addClass", "removeClass"], function()
    var _oldFn = $.fn[this];
    $.fn[this] = function()
        var hidden = this.find(":hidden").add(this.filter(":hidden"));
        var result = _oldFn.apply(this, arguments);
        hidden.filter(":visible").each(function()
            $(this).triggerHandler("show"); //No bubbling
        );
        return result;
    
);

现在你的元素:

$("#myLazyUl").bind("show", function()
    alert(this);
);

您可以通过将其他 jQuery 函数添加到顶部的数组中来添加覆盖(例如“attr”)

【讨论】:

【参考方案8】:

基于 Glenns ideea 的隐藏/显示事件触发器: 删除了切换,因为它会触发显示/隐藏,我们不希望一个事件发生 2 次触发

$(function()
    $.each(["show","hide", "toggleClass", "addClass", "removeClass"], function()
        var _oldFn = $.fn[this];
        $.fn[this] = function()
            var hidden = this.find(":hidden").add(this.filter(":hidden"));
            var visible = this.find(":visible").add(this.filter(":visible"));
            var result = _oldFn.apply(this, arguments);
            hidden.filter(":visible").each(function()
                $(this).triggerHandler("show");
            );
            visible.filter(":hidden").each(function()
                $(this).triggerHandler("hide");
            );
            return result;
        
    );
);

【讨论】:

是否也要检查 attr 和 removeAttr ?【参考方案9】:

我遇到了同样的问题,并创建了一个 jQuery 插件来为我们的网站解决它。

https://github.com/shaunbowe/jquery.visibilityChanged

根据您的示例,您将如何使用它:

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

【讨论】:

轮询效率不高,如果用于许多元素,会显着降低浏览器的速度。【参考方案10】:

在这里帮助我的是最近的ResizeObserver spec polyfill:

const divEl = $('#section60');

const ro = new ResizeObserver(() => 
    if (divEl.is(':visible')) 
        console.log("it's visible now!");
    
);
ro.observe(divEl[0]);

请注意,它是跨浏览器和高性能(无轮询)。

【讨论】:

与其他人不同,每次显示/隐藏表格行时都能很好地检测到,另外一个优点是不需要插件!【参考方案11】:

我做了一个简单的 setinterval 函数来实现这一点。如果具有类 div1 的元素可见,则将 div2 设置为可见。我知道不是什么好方法,而是一个简单的解决方法。

setInterval(function()
  if($('.div1').is(':visible'))
    $('.div2').show();
  
  else 
    $('.div2').hide();
        
, 100);

【讨论】:

【参考方案12】:

使用 jQuery 航点:

$('#contentDiv').waypoint(function() 
   alert('do something');
);

the site of jQuery Waypoints 上的其他示例。

【讨论】:

这仅在项目由于滚动而不是由于其他程序更改而变得可见时才有效。 @AdamJones 当我使用键盘时,它按预期工作。例如:imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll 糟糕的,几乎不存在的文档。而且似乎没有任何方法可以检测该项目何时在屏幕上可见。这个库只抛出一个事件,所以你需要另一个库来进行屏幕可见性检测,这使得它毫无用处。【参考方案13】:

只需将触发器与选择器绑定,并将代码放入触发器事件中:

jQuery(function() 
  jQuery("#contentDiv:hidden").show().trigger('show');

  jQuery('#contentDiv').on('show', function() 
    console.log('#contentDiv is now visible');
    // your code here
  );
);

【讨论】:

我认为这是一个非常优雅的解决方案。它对我有用。【参考方案14】:

您也可以尝试jQuery appear plugin,如并行线程https://***.com/a/3535028/741782中所述

【讨论】:

【参考方案15】:

这支持动画完成后的缓动和触发事件! [在 jQuery 2.2.4 上测试]

(function ($) 
    $.each(['show', 'hide', 'fadeOut', 'fadeIn'], function (i, ev) 
        var el = $.fn[ev];
        $.fn[ev] = function () 
            var result = el.apply(this, arguments);
            var _self=this;
            result.promise().done(function () 
                _self.triggerHandler(ev, [result]);
                //console.log(_self);
            );
            return result;
        ;
    );
)(jQuery);

灵感来自http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/

【讨论】:

【参考方案16】:

有一个 jQuery 插件可用于观察 DOM 属性的变化,

https://github.com/darcyclarke/jQuery-Watch-Plugin

插件封装了所有你需要做的就是绑定MutationObserver

然后您可以使用它来观看 div:

$("#selector").watch('css', function() 
    console.log("Visibility: " + this.style.display == 'none'?'hidden':'shown'));
    //or any random events
);

【讨论】:

【参考方案17】:

希望这将以最简单的方式完成工作:

$("#myID").on('show').trigger('displayShow');

$('#myID').off('displayShow').on('displayShow', function(e) 
    console.log('This event will be triggered when myID will be visible');
);

【讨论】:

【参考方案18】:

我根据 Glenns 的想法更改了 Catalint 的隐藏/显示事件触发器。 我的问题是我有一个模块化应用程序。我在显示和隐藏 div 父级的模块之间切换。然后,当我隐藏一个模块并显示另一个模块时,使用他的方法在模块之间切换时会出现明显的延迟。我只需要偶尔点亮这个事件,并在一些特殊的孩子身上。所以我决定只通知“displayObserver”类的孩子

$.each(["show", "hide", "toggleClass", "addClass", "removeClass"], function () 
    var _oldFn = $.fn[this];
    $.fn[this] = function () 
        var hidden = this.find(".displayObserver:hidden").add(this.filter(":hidden"));
        var visible = this.find(".displayObserver:visible").add(this.filter(":visible"));
        var result = _oldFn.apply(this, arguments);
        hidden.filter(":visible").each(function () 
            $(this).triggerHandler("show");
        ); 
        visible.filter(":hidden").each(function () 
            $(this).triggerHandler("hide");
        );
        return result;
    
);

然后当一个孩子想听“显示”或“隐藏”事件时,我必须给他添加“displayObserver”类,当它不想继续听时,我删除他的类

bindDisplayEvent: function () 
   $("#child1").addClass("displayObserver");
   $("#child1").off("show", this.onParentShow);
   $("#child1").on("show", this.onParentShow);
,

bindDisplayEvent: function () 
   $("#child1").removeClass("displayObserver");
   $("#child1").off("show", this.onParentShow);
,

希望有帮助

【讨论】:

【参考方案19】:

一种方法。 仅适用于由 css 类更改引起的可见性更改,但也可以扩展以监视属性更改。

var observer = new MutationObserver(function(mutations) 
        var clone = $(mutations[0].target).clone();
        clone.removeClass();
                for(var i = 0; i < mutations.length; i++)
                    clone.addClass(mutations[i].oldValue);
        
        $(document.body).append(clone);
        var cloneVisibility = $(clone).is(":visible");
        $(clone).remove();
        if (cloneVisibility != $(mutations[0].target).is(":visible"))
            var visibilityChangedEvent = document.createEvent('Event');
            visibilityChangedEvent.initEvent('visibilityChanged', true, true);
            mutations[0].target.dispatchEvent(visibilityChangedEvent);
        
);

var targets = $('.ui-collapsible-content');
$.each(targets, function(i,target)
        target.addEventListener('visibilityChanged',VisbilityChanedEventHandler);
        target.addEventListener('DOMNodeRemovedFromDocument',VisbilityChanedEventHandler );
        observer.observe(target,  attributes: true, attributeFilter : ['class'], childList: false, attributeOldValue: true );
    );

function VisbilityChanedEventHandler(e)console.log('Kaboom babe'); console.log(e.target); 

【讨论】:

【参考方案20】:

我的解决方案:

; (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

【讨论】:

【参考方案21】:
$( window ).scroll(function(e,i) 
    win_top = $( window ).scrollTop();
    win_bottom = $( window ).height() + win_top;
    //console.log( win_top,win_bottom );
    $('.onvisible').each(function()
    
        t = $(this).offset().top;
        b = t + $(this).height();
        if( t > win_top && b < win_bottom )
            alert("do something");
    );
);

【讨论】:

【参考方案22】:
<div id="welcometo">Özhan</div>
<input type="button" name="ooo" 
       onclick="JavaScript:
                    if(document.all.welcometo.style.display=='none') 
                        document.all.welcometo.style.display='';
                     else 
                        document.all.welcometo.style.display='none';
                    ">

此代码自动控制不需要查询可见或不可见控件

【讨论】:

以上是关于让 div 可见时触发操作的 jQuery 事件的主要内容,如果未能解决你的问题,请参考以下文章

jQuery 如何获取到隐藏元素的高度?或者在dom元素可见性改变时能触发个事件也行。

JS问题/jquery问题 如何让DIV里的元素不触发mouseover事件?

jquery 怎么判断 某个div 是不是进入可视区域,或者是滚动条拖拽到某个位置时,出发某个事件!

如何监听div内容的改变?

jquery停止子触发父事件

JQuery如何监听一个DIV宽高的变化?