Acara jQuery untuk memicu tindakan ketika div dibuat terlihat

312

Saya menggunakan jQuery di situs saya dan saya ingin memicu tindakan tertentu ketika div tertentu dibuat terlihat.

Apakah mungkin untuk melampirkan semacam event handler "isvisible" ke div sewenang-wenang dan menjalankan kode tertentu ketika div dibuat terlihat?

Saya ingin sesuatu seperti kodesemu berikut:

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

Kode lansiran ("lakukan sesuatu") tidak boleh diaktifkan sampai contentDiv benar-benar terlihat.

Terima kasih.

frankadelic
sumber
Lihat di sini: stackoverflow.com/questions/1462138/…
Anderson Green

Jawaban:

190

Anda selalu dapat menambahkan metode .show () asli sehingga Anda tidak harus memicu acara setiap kali Anda menunjukkan sesuatu atau jika Anda membutuhkannya untuk bekerja dengan kode lawas:

Ekstensi 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]);
    });
  }
});

Contoh penggunaan:

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

Ini secara efektif memungkinkan Anda melakukan sesuatu beforeShow dan afterShow sambil tetap menjalankan perilaku normal dari metode .show () yang asli.

Anda juga bisa membuat metode lain sehingga Anda tidak perlu mengganti metode .show () yang asli.

Tres
sumber
7
EDIT: Hanya ada satu kelemahan dengan metode ini: Anda harus mengulangi "ekstensi" yang sama untuk semua metode yang mengungkapkan elemen: show (), slideDown () dll. Sesuatu yang lebih universal diperlukan untuk menyelesaikan masalah ini untuk sekali dan semua, karena tidak mungkin untuk memiliki acara "siap" untuk didelegasikan () atau langsung ().
Shahriyar Imanov
1
Bagus, satu-satunya masalah adalah bahwa fadeTofungsi tidak berfungsi dengan baik setelah menerapkan fungsi ini
Omid
9
Kode Anda tampaknya tidak berfungsi dengan jQuery terbaru (1.7.1 pada tanggal komentar ini). Saya telah mengerjakan ulang solusi ini sedikit agar bekerja dengan jQuery terbaru: stackoverflow.com/a/9422207/135968
mkmurray
1
Tidak dapat membuat kode itu berfungsi dengan visibilitas div yang dipicu oleh respons ajax.
JackTheKnife
Pemula di sini. Saya tidak mengerti mengapa obj (dan newCallback) dapat direferensikan di luar deklarasi function () seperti dapat dilihat pada apply (). Saya diajari bahwa mendeklarasikan dengan var membuat variabel lokal, tetapi menghapus "var" membuatnya menjadi global.
Yuta73
85

Masalahnya sedang ditangani oleh pengamat mutasi DOM . Mereka memungkinkan Anda untuk mengikat pengamat (fungsi) ke acara mengubah konten, teks atau atribut elemen dom.

Dengan rilis IE11, semua browser utama mendukung fitur ini, periksa http://caniuse.com/mutationobserver

Contoh kode adalah sebagai berikut:

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

hegemon
sumber
3
Sayang sekali IE belum mendukungnya. caniuse.com/mutationobserver -> Untuk melihat browser yang mendukungnya.
ccsakuweb
2
Ini memang bekerja, dan saya tidak perlu mendukung browser lama, jadi itu sempurna! I'br menambahkan bukti JSFiddle dari jawabannya di sini: jsfiddle.net/DanAtkinson/26URF
Dan Atkinson
berfungsi dengan baik di chrome tetapi tidak berfungsi di blackberry 10 cascade webview (jika ada yang peduli;))
Guillaume Gendre
1
Ini tampaknya tidak berfungsi jika perubahan visibilitas disebabkan oleh perubahan atribut pada leluhur yang dipantau.
Michael
4
Ini sepertinya tidak berfungsi di Chrome 51, tidak yakin mengapa. Menjalankan kode di atas dan menekan tombol, tidak ada peringatan.
Kris
76

Tidak ada acara asli yang dapat Anda kaitkan untuk ini, tetapi Anda dapat memicu suatu peristiwa dari skrip Anda setelah Anda membuat div terlihat menggunakan. fungsi pemicu

misalnya

//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');
});
kotak merah
sumber
45
Keterbatasan saya di sini adalah bahwa saya tidak perlu memiliki akses ke kode yang menunjukkan div saya (). Jadi saya tidak akan bisa memanggil metode trigger ().
frankadelic
11
JS disediakan oleh tim pengembangan di luar organisasi saya. Itu juga semacam "kotak hitam", jadi kami tidak ingin mengubah kode itu jika memungkinkan. Ini mungkin satu-satunya pilihan kita.
frankadelic
Anda selalu dapat menghapus fungsi js mereka dengan implementasi Anda sendiri. Kedengarannya suram!
redsquare
11
@redsquare: Bagaimana jika show()dipanggil dari beberapa tempat selain dari blok kode yang dibahas di atas?
Robin Maben
1
Untuk contoh ini, Anda harus mengubah nama fungsi menjadi onIsVisiblekarena penggunaan "isVisible" sedikit ambigu sekarang.
Brad Johnson
27

Anda dapat menggunakan plugin Live Query jQuery . Dan tulis kode sebagai berikut:

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

Maka setiap kali contentDiv terlihat, "lakukan sesuatu" akan diperingatkan!

ax003d
sumber
Yah, itu berhasil. Saya telah memikirkannya dan menolaknya karena tidak mungkin berhasil, tanpa mencobanya. Seharusnya sudah mencobanya. :)
neminem
1
Tidak bekerja untuk saya. Saya mendapatkan kesalahan "livequery bukan fungsi". Mencoba dengan "jquery-1.12.4.min.js" dan "jquery-3.1.1.min.js"
Paul Gorbas
2
@ Paul: Ini adalah plugin
Christian
Ini dapat sangat memperlambat situs web Anda! Plugin livequery melakukan polling cepat pada atribut, daripada menggunakan metode efisien modern seperti pengamat mutasi DOM. Jadi saya lebih suka solusi @hegemon: stackoverflow.com/a/16462443/19163 (dan gunakan polling hanya sebagai fallback untuk versi IE lama) - vog 1 jam yang lalu
vog
20

Solusi redsquare adalah jawaban yang tepat.

Tetapi sebagai solusi IN-THEORY Anda dapat menulis fungsi yang memilih elemen yang dikelompokkan berdasarkan .visibilityCheck( tidak semua elemen yang terlihat ) dan memeriksa visibilitynilai propertinya; jika truekemudian lakukan sesuatu.

Setelah itu, fungsi tersebut harus dilakukan secara berkala menggunakan setInterval()fungsi tersebut. Anda dapat menghentikan timer menggunakan clearInterval()panggilan keluar yang berhasil.

Ini sebuah contoh:

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

window.setInterval(foo, 100);

Anda juga dapat melakukan beberapa peningkatan kinerja di atasnya, namun solusinya pada dasarnya tidak masuk akal untuk digunakan dalam tindakan. Begitu...

sepehr
sumber
5
bukan bentuk yang baik untuk menggunakan fungsi tersirat di dalam setTimeout / setInterval. Gunakan setTimeout (foo, 100)
redsquare
1
Saya harus menyerahkannya kepada Anda, ini kreatif, meskipun tidak menyenangkan. Dan itu cukup cepat dan kotor untuk melakukan trik bagi saya dengan cara yang sesuai dengan IE8. Terima kasih!
JD Smith
atau display:none?
Michael
12

Kode berikut (ditarik dari http://maximeparmentier.com/2012/11/06/bind-show-hide-events-with-jquery/ ) akan memungkinkan Anda untuk menggunakan $('#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);
JellicleCat
sumber
8
Ini berfungsi dengan baik untuk saya, tetapi penting untuk dicatat bahwa fungsi istirahat rantai pada acara dan fungsi sembunyikan, yang merusak banyak plugin. Tambahkan pengembalian di depan el.apply(this, arguments)untuk memperbaikinya.
jaimerump
Ini yang saya cari! Pengembalian perlu ditambahkan seperti dalam komentar dari @jaimerump
Brainfeeder
9

Jika Anda ingin memicu acara di semua elemen (dan elemen turunan) yang benar-benar dibuat terlihat, oleh $ .show, toggle, toggleClass, addClass, atau 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;
    }
});

Dan sekarang elemen Anda:

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

Anda bisa menambahkan penggantian ke fungsi jQuery tambahan dengan menambahkannya ke array di bagian atas (seperti "attr")

Glenn Lane
sumber
9

pemicu acara sembunyikan / tunjukkan berdasarkan Glenns ideea: dihapus toggle karena ia memunculkan / sembunyikan dan kami tidak ingin 2 api untuk satu acara

$(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;
        }
    });
});
catalint
sumber
2
Anda juga harus memeriksa attr dan removeAttr?
Andrija
5

Saya punya masalah yang sama dan membuat plugin jQuery untuk menyelesaikannya untuk situs kami.

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

Inilah cara Anda menggunakannya berdasarkan contoh Anda:

$('#contentDiv').visibilityChanged(function(element, visible) {
    alert("do something");
});
Shaun Bowe
sumber
1
Polling tidak terlalu efisien, dan secara signifikan akan memperlambat browser jika itu digunakan untuk banyak elemen.
Cerin
4

Gunakan jQuery Waypoints:

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

Contoh lain di situs jQuery Waypoints .

Fedir RYKHTIK
sumber
1
Ini hanya berfungsi ketika item menjadi terlihat karena bergulir dan bukan karena perubahan progmatik lainnya.
AdamJones
@AdamJones Saat saya menggunakan keyboard, itu berfungsi seperti yang diharapkan. Mis .: imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll
Fedir RYKHTIK
4

Apa yang membantu saya di sini adalah ResizeObserver spec polyfill baru-baru ini :

const divEl = $('#section60');

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

Perhatikan bahwa ini adalah crossbrowser dan performant (tidak ada polling).

Artem Vasiliev
sumber
Bekerja dengan baik untuk mendeteksi setiap kali baris tabel ditampilkan / disembunyikan tidak seperti yang lain, juga plus adalah tidak ada plugin yang diperlukan!
BrettC
3

Saya melakukan fungsi setinterval sederhana untuk mencapai ini. Jika elemen dengan kelas div1 terlihat, itu mengatur div2 agar terlihat. Saya tahu bukan metode yang baik, tetapi perbaikan sederhana.

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

Dukungan ini memudahkan dan memicu acara setelah animasi selesai! [diuji pada 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);

Terinspirasi Oleh http://viralpatel.net/blogs/jquery-trigger-custom-event-show-hide-element/

MSS
sumber
2

Cukup ikat pemicu dengan pemilih dan masukkan kode ke dalam peristiwa pemicu:

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

  jQuery('#contentDiv').on('show', function() {
    console.log('#contentDiv is now visible');
    // your code here
  });
});
Nikhil Gyan
sumber
Saya pikir ini solusi yang cukup elegan. Ini berhasil untuk saya.
Perangkat Lunak KIKO
1

Ada plugin jQuery yang tersedia untuk menonton perubahan atribut DOM,

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

Plugin membungkus Yang perlu Anda lakukan adalah mengikat MutationObserver

Anda kemudian dapat menggunakannya untuk menonton div menggunakan:

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

Semoga ini akan melakukan pekerjaan dengan cara paling sederhana:

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

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

Saya mengubah pemicu acara sembunyikan / tampilkan dari Catalint berdasarkan ide Glenns. Masalah saya adalah saya punya aplikasi modular. Saya mengubah antara modul yang menunjukkan dan menyembunyikan orangtua divs. Kemudian ketika saya menyembunyikan modul dan menunjukkan yang lain, dengan metodenya saya memiliki keterlambatan yang terlihat ketika saya mengubah antar modul. Saya hanya kadang-kadang perlu menyulut acara ini, dan pada beberapa anak spesial. Jadi saya memutuskan untuk memberi tahu anak-anak dengan kelas "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;
    }
});

Kemudian ketika seorang anak ingin mendengarkan acara "show" atau "hide" saya harus menambahkan dia kelas "displayObserver", dan ketika itu tidak ingin melanjutkan mendengarkannya, saya menghapusnya kelas

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

Saya berharap bantuan

ccsakuweb
sumber
0

Salah satu cara untuk melakukan ini.
Hanya berfungsi pada perubahan visibilitas yang dibuat oleh perubahan kelas css, tetapi dapat diperluas untuk mengawasi perubahan atribut juga.

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); }
Matas Vaitkevicius
sumber
0

solusi saya:

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

contoh penggunaan:

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

function processMoreLessButton(){
//some logic
}
pengguna2699000
sumber
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");
    });
});
Saifullah khan
sumber
-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';
                    }">

Kontrol otomatis kode ini tidak memerlukan kueri kontrol yang terlihat atau tidak terlihat

alpc
sumber