Sự kiện jQuery để kích hoạt hành động khi một div được hiển thị


312

Tôi đang sử dụng jQuery trong trang web của mình và tôi muốn kích hoạt một số hành động nhất định khi một div nhất định được hiển thị.

Có thể đính kèm một số loại xử lý sự kiện "isvisible" vào các div tùy ý và có mã nhất định chạy khi chúng được hiển thị không?

Tôi muốn một cái gì đó giống như mã giả sau đây:

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

Mã cảnh báo ("làm gì đó") không nên kích hoạt cho đến khi nội dungDiv thực sự hiển thị.

Cảm ơn.


Câu trả lời:


190

Bạn luôn có thể thêm vào phương thức .show () ban đầu để bạn không phải kích hoạt các sự kiện mỗi khi bạn hiển thị thứ gì đó hoặc nếu bạn cần nó để làm việc với mã kế thừa:

Gia hạn 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]);
    });
  }
});

Ví dụ sử dụng:

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

Điều này cho phép bạn thực hiện một cái gì đó trướcShow và afterShow trong khi vẫn thực hiện hành vi bình thường của phương thức .show () ban đầu.

Bạn cũng có thể tạo một phương thức khác để bạn không phải ghi đè phương thức .show () ban đầu.


7
EDIT: Chỉ có một nhược điểm với phương thức này: Bạn sẽ phải lặp lại cùng một "phần mở rộng" cho tất cả các phương thức tiết lộ phần tử: show (), slideDown (), v.v. tất cả, vì không thể có sự kiện "sẵn sàng" cho đại biểu () hoặc trực tiếp ().
Shahriyar Imanov

1
Tốt, vấn đề duy nhất là fadeTochức năng không hoạt động đúng sau khi thực hiện chức năng này
Omid 11/11/11

9
Mã của bạn dường như không hoạt động với jQuery mới nhất (1.7.1 tại ngày nhận xét này). Tôi đã làm lại giải pháp này một chút để làm việc với jQuery mới nhất: stackoverflow.com/a/9422207/135968
mkm Hur

1
Không thể làm cho mã đó hoạt động với khả năng hiển thị div được kích hoạt bởi phản hồi ajax.
JackTheKnife

Newbie đây. Tôi không hiểu tại sao obj (và newCallback) có thể được tham chiếu bên ngoài khai báo hàm () như có thể thấy trong áp dụng (). Tôi đã được dạy rằng khai báo với var làm cho các biến cục bộ, nhưng loại bỏ "var" làm cho chúng tự động toàn cầu.
Yuta73

85

Vấn đề đang được giải quyết bởi các nhà quan sát đột biến DOM . Chúng cho phép bạn liên kết một người quan sát (một chức năng) với các sự kiện thay đổi nội dung, văn bản hoặc thuộc tính của các yếu tố dom.

Với việc phát hành IE11, tất cả các trình duyệt chính đều hỗ trợ tính năng này, hãy kiểm tra http://caniuse.com/mutingobserver

Mã ví dụ như sau:

$(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
Thật đáng tiếc khi IE chưa hỗ trợ nó. caniuse.com/mutingobserver -> Để xem các trình duyệt hỗ trợ nó.
ccsakuweb

2
Điều này thực sự hoạt động và tôi không cần phải hỗ trợ các trình duyệt cũ nên rất hoàn hảo! Tôi đã thêm một bằng chứng về câu trả lời của JSFiddle
Dan Atkinson

hoạt động tốt trong chrome nhưng không hoạt động trong webview tầng 10 của blackberry (nếu có ai quan tâm;))
Guillaume Gendre

1
Điều này dường như không hoạt động nếu thay đổi khả năng hiển thị được gây ra bởi sự thay đổi thuộc tính trong tổ tiên của người được theo dõi.
Michael

4
Điều này dường như không hoạt động trong Chrome 51, không biết tại sao. Chạy mã trên và nhấn nút, không có cảnh báo.
Kris

76

Không có sự kiện riêng nào bạn có thể tham gia vào việc này tuy nhiên bạn có thể kích hoạt một sự kiện từ tập lệnh của mình sau khi bạn đã hiển thị div bằng cách sử dụng. chức năng kích hoạt

ví dụ

//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
Hạn chế của tôi ở đây là tôi không nhất thiết phải có quyền truy cập vào mã hiển thị () là div của tôi. Vì vậy, tôi sẽ không thể thực sự gọi phương thức trigger ().
frankadelic

11
JS được cung cấp bởi một nhóm phát triển bên ngoài tổ chức của tôi. Đó cũng là một cái gì đó của "hộp đen", vì vậy chúng tôi không muốn sửa đổi mã đó nếu có thể. Nó có thể là sự lựa chọn duy nhất của chúng tôi mặc dù.
frankadelic

bạn luôn có thể đóng dấu các chức năng js của họ bằng cách thực hiện của riêng bạn. Nghe có vẻ nghiệt ngã!
redsapes

11
@redsapes: Điều gì xảy ra nếu show()được gọi từ nhiều nơi khác ngoài khối mã được thảo luận ở trên?
Robin Maben

1
Trong ví dụ này, bạn nên thay đổi tên hàm thành onIsVisiblevì việc sử dụng "isVisible" hơi mơ hồ ngay bây giờ.
Brad Johnson

27

Bạn có thể sử dụng plugin Live Query của jQuery . Và viết mã như sau:

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

Sau đó, mỗi khi nội dungDiv hiển thị, "làm gì đó" sẽ được cảnh báo!


Vâng dang, hoạt động. Tôi đã nghĩ về nó và từ chối nó như không thể làm việc, mà không thử nó. Nên đã thử nó. :)
neminem

1
Không làm việc cho tôi. Tôi gặp lỗi "livequery không phải là một chức năng". Đã thử với cả "jquery-1.12.4.min.js" và "jquery-3.1.1.min.js"
Paul Gorbas

2
@Paul: Đây là một plugin
Christian

Điều này có thể làm chậm đáng kể trang web của bạn! Plugin livequery thực hiện bỏ phiếu nhanh trên các thuộc tính, thay vì sử dụng các phương thức hiệu quả hiện đại như trình quan sát đột biến DOM. Vì vậy, tôi thích giải pháp của @hegemon: stackoverflow.com/a/16462443/19163 (và chỉ sử dụng bỏ phiếu làm dự phòng cho các phiên bản IE cũ) - vog 1 giờ trước
vog

20

giải pháp của redsapes là câu trả lời đúng.

Nhưng là một giải pháp IN-THEORY, bạn có thể viết một hàm đang chọn các phần tử được phân loại theo .visibilityCheck( không phải tất cả các phần tử hiển thị ) và kiểm tra visibilitygiá trị thuộc tính của chúng ; nếu truesau đó làm một cái gì đó.

Sau đó, chức năng nên được thực hiện định kỳ bằng cách sử dụng setInterval()chức năng. Bạn có thể dừng bộ hẹn giờ bằng cách clearInterval()gọi thành công.

Đây là một ví dụ:

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

window.setInterval(foo, 100);

Bạn cũng có thể thực hiện một số cải tiến hiệu suất trên nó, tuy nhiên, giải pháp về cơ bản là vô lý khi được sử dụng trong thực tế. Vì thế...


5
hình thức không tốt để sử dụng một func ngụ ý bên trong setTimeout / setInterval. Sử dụng setTimeout (foo, 100)
redsapes

1
Tôi phải trao nó cho bạn, đây là sáng tạo, mặc dù khó chịu. Và nó chỉ đủ nhanh và bẩn để thực hiện thủ thuật cho tôi theo cách tuân thủ IE8. Cảm ơn bạn!
JD Smith

hay display:none?
Michael

12

Mã sau đây (được lấy từ http://maximeparmentier.com/2012/11/06/bind-show- leather-event-with-jquery / ) sẽ cho phép bạn sử dụng $('#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
Điều này hoạt động hoàn hảo đối với tôi, nhưng điều quan trọng cần lưu ý là chức năng phá vỡ chuỗi trong chương trình và ẩn các chức năng, làm hỏng rất nhiều plugin. Thêm một sự trở lại ở phía trước el.apply(this, arguments)để sửa lỗi này.
jaimerump

Đây là những gì tôi đang tìm kiếm! Sự trở lại cần phải được thêm vào như trong nhận xét từ @jaimerump
Brainfeeder

9

Nếu bạn muốn kích hoạt sự kiện trên tất cả các yếu tố (và các yếu tố con) thực sự hiển thị, bằng $ .show, chuyển đổi, toggleClass, addClass hoặc 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;
    }
});

Và bây giờ yếu tố của bạn:

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

Bạn có thể thêm ghi đè vào các hàm jQuery bổ sung bằng cách thêm chúng vào mảng ở trên cùng (như "attr")


9

một trình kích hoạt sự kiện ẩn / hiển thị dựa trên idennea của Glenns: đã loại bỏ chuyển đổi vì nó kích hoạt chương trình / ẩn và chúng tôi không muốn bắn 2 lần cho một sự kiện

$(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
bạn cũng nên kiểm tra attr và removeAttr chứ?
Andrija

5

Tôi đã có vấn đề tương tự và tạo ra một plugin jQuery để giải quyết nó cho trang web của chúng tôi.

https://github.com/shaunbowe/jquery.visibility Thay đổi

Đây là cách bạn sẽ sử dụng nó dựa trên ví dụ của bạn:

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

1
Bỏ phiếu không hiệu quả lắm và sẽ làm chậm đáng kể trình duyệt nếu nó được sử dụng cho nhiều yếu tố.
Cerin

4

Sử dụng các điểm tham chiếu của jQuery:

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

Các ví dụ khác trên trang web của Waypoint jQuery .


1
Điều này chỉ hoạt động khi mục trở nên hiển thị do cuộn và không phải do các thay đổi progmatic khác.
AdamJones

@AdamJones Khi tôi sử dụng bàn phím, nó hoạt động như mong đợi. Ex .: imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll
Fedir RYKHTIK

4

Điều giúp tôi ở đây là polyfill spec ResizeObserver gần đây :

const divEl = $('#section60');

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

Lưu ý rằng đó là crossbrowser và hiệu suất (không bỏ phiếu).


Hoạt động tốt để phát hiện mỗi khi một hàng của bảng được hiển thị / ẩn không giống như các hàng khác, cũng có một điểm cộng là không có plugin yêu cầu!
BrettC

3

Tôi đã làm một hàm setinterval đơn giản để đạt được điều này. Nếu phần tử với lớp div1 hiển thị, nó đặt div2 hiển thị. Tôi biết không phải là một phương pháp tốt, nhưng một sửa chữa đơn giản.

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


2

Điều này hỗ trợ nới lỏng và kích hoạt sự kiện sau khi hoạt hình được thực hiện! [đã thử nghiệm trên 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);

Lấy cảm hứng từ http://viruspatel.net/bloss/jquery-trigger-custom-event-show- leather-element /


2

Chỉ cần liên kết một trình kích hoạt với bộ chọn và đặt mã vào sự kiện kích hoạt:

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

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

Tôi nghĩ rằng đây là một giải pháp khá thanh lịch. Nó làm việc cho tôi.
Phần mềm KIKO

1

Có một plugin jQuery có sẵn để xem sự thay đổi trong các thuộc tính DOM,

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

Plugin kết thúc tốt đẹp Tất cả những gì bạn cần làm là liên kết MutingObserver

Sau đó, bạn có thể sử dụng nó để xem div bằng cách sử dụng:

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

1

Hy vọng điều này sẽ làm công việc theo cách đơn giản nhất:

$("#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

Tôi đã thay đổi trình kích hoạt ẩn / hiển thị từ Catalint dựa trên ý tưởng của Glenns. Vấn đề của tôi là tôi có một ứng dụng mô-đun. Tôi thay đổi giữa các mô-đun hiển thị và ẩn cha mẹ div. Sau đó, khi tôi ẩn một mô-đun và hiển thị một mô-đun khác, với phương thức của nó, tôi có độ trễ hiển thị khi tôi thay đổi giữa các mô-đun. Đôi khi tôi chỉ cần thắp sáng sự kiện này, và ở một số đứa trẻ đặc biệt. Vì vậy, tôi quyết định chỉ thông báo cho những đứa trẻ với lớp "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;
    }
});

Sau đó, khi một đứa trẻ muốn nghe sự kiện "hiển thị" hoặc "ẩn", tôi phải thêm cho nó lớp "displayObserver" và khi nó không muốn tiếp tục nghe nó, tôi sẽ loại bỏ nó

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);
},

Tôi muốn giúp đỡ


0

Một cách để làm điều này.
Chỉ hoạt động trên các thay đổi mức độ hiển thị được thực hiện bởi thay đổi lớp css, nhưng cũng có thể được mở rộng để theo dõi các thay đổi thuộc tính.

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

giải pháp của tôi:

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

sử dụng ví dụ:

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

Mã này kiểm soát tự động không yêu cầu truy vấn có thể nhìn thấy hoặc kiểm soát không mong muốn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.