使div可见时的jQuery事件触发动作


312

我在网站上使用jQuery,当某个div可见时,我想触发某些动作。

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

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

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

在真正使contentDiv可见之前,将不会触发alert(“做某事”)代码。

谢谢。


Answers:


190

您总是可以添加到原始的.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()方法。


7
编辑:此方法只有一个缺点:您将必须对揭示元素的所有方法重复相同的“扩展”:show(),slideDown()等。需要更通用的方法才能一次性解决此问题,所有,因为不可能有委托()或live()的“就绪”事件。
Shahriyar Imanov

1
不错,唯一的问题是,fadeTo功能无法正常实现此功能后才能正常运行
奥米德

9
您的代码似乎不适用于最新的jQuery(在本文发表之日为1.7.1)。我对这个解决方案进行了略微的修改,以使用最新的jQuery:stackoverflow.com/a/9422207/135968
mkmurray,2012年

1
无法使该代码与Ajax响应触发的div可见性一起工作。
JackTheKnife

新手在这里。我不明白为什么obj(和newCallback)可以在function()声明之外引用,就像在apply()中可以看到的那样。有人告诉我,用var声明会使变量成为局部变量,但是删除“ var”会使变量自动成全局变量。
Yuta73年

85

DOM突变观察者正在解决该问题。它们允许您将观察者(函数)绑定到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="http://code.jquery.com/jquery-1.9.1.min.js"></script>


3
遗憾的是IE还不支持它。caniuse.com/mutationobserver- >查看支持它的浏览器。
ccsakuweb

2
这确实可以工作,并且我不需要支持旧版浏览器,因此非常完美!我在这里添加了答案的JSFiddle证明:jsfiddle.net/DanAtkinson/26URF
Dan Atkinson

在chrome中效果很好,但在blackberry 10级联webview中效果不佳(如果有人关心;))
Guillaume Gendre 2013年

1
如果可见性更改是由被监视对象的祖先的属性更改引起的,则这似乎不起作用。
迈克尔

4
这似乎不适用于Chrome 51,不确定原因。运行上面的代码并按下按钮,没有警报。
克里斯(Kris)

76

没有本地事件可以挂接到此事件,但是使用可以使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');
});

45
我在这里的局限性是,我不必访问show()是我的div的代码。因此,我将无法实际调用trigger()方法。
frankadelic

11
JS由我组织外部的开发团队提供。它也是一个“黑匣子”,因此我们不希望尽可能修改该代码。虽然这可能是我们唯一的选择。
frankadelic

您始终可以使用自己的实现在其js函数上进行标记。听起来很严峻!
redsquare

11
@redsquare:如果show()从上述代码块之外的多个地方调用if,该怎么办?
罗宾·马本

1
对于此示例,您应该将函数名称更改为,onIsVisible因为现在对“ isVisible”的使用有点模棱两可。
布拉德·约翰逊

27

您可以使用jQuery的Live Query插件。并编写如下代码:

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

然后,每当contentDiv可见时,都会提醒“做某事”!


好吧,那行得通。我已经考虑过了,并且拒绝了它,因为不尝试就不可能工作。应该尝试过的。:)
neminem '16

1
没为我工作。我收到错误消息“ livequery不是函数”。尝试同时使用“ jquery-1.12.4.min.js”和“ jquery-3.1.1.min.js”
Paul Gorbas,2016年

2
@Paul:这是一个插件
Christian

这可能会大大降低您的网站速度!livequery插件对属性执行快速轮询,而不使用诸如DOM突变观察器之类的现代有效方法。因此,我希望使用@hegemon的解决方案:stackoverflow.com/a/16462443/19163(并且仅将轮询用作旧IE版本的备用)– vog 1小时前
vog

20

redsquare的解决方案是正确的答案。

但是,作为一个IN-THEORY解决方案,您可以编写一个函数,该函数选择按分类的元素.visibilityCheck并非所有可见元素)并检查其visibility属性值。如果true那么做。

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

这是一个例子:

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

window.setInterval(foo, 100);

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


5
在setTimeout / setInterval中使用隐式func的不好形式。使用setTimeout(foo,100)
Redsquare

1
我必须把它交给你,这虽然很讨厌,但是很有创意。而且它又快又脏,足以以符合IE8的方式为我解决问题。谢谢!
JD史密斯

还是display:none
迈克尔

12

以下代码(从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);

8
这对我来说效果很好,但是需要注意的是,该函数中断了show和hide函数的链接,从而破坏了许多插件。在前面添加退货el.apply(this, arguments)即可解决此问题。
jaimerump

这就是我想要的!需要添加返回值,如@jaimerump的评论
Brainfeeder 2014年

9

如果要在实际上变为可见的所有元素(和子元素)上触发事件,请通过$ .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”)


9

基于Glenns想法的隐藏/显示事件触发器:删除了切换,因为它触发了显示/隐藏,我们不希望一次触发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;
        }
    });
});

2
您还应该检查attr和removeAttr吗?
2012年



4

在这里帮助我的是最近的ResizeObserver规格polyfill

const divEl = $('#section60');

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

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


与其他人每次显示/隐藏表格行时一样,可以很好地进行检测,另外还有一个好处是,它不需要插件!
BrettC

3

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

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


2

动画完成后,此支持缓动和触发事件![在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/


2

只需将触发器与选择器绑定,然后将代码放入触发事件即可:

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

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

我认为这是一个很好的解决方案。它为我工作。
KIKO Software

1

有一个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
});

1

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

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

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

0

我根据Glenns的想法更改了Catalint的hide / show事件触发器。我的问题是我有一个模块化应用程序。我在显示和隐藏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);
},

希望有帮助


0

一种方法。
仅适用于由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); }

0

我的解决方案:

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

0
$( 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");
    });
});

-2
<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';
                    }">

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.