jQuery подія, щоб запустити дію, коли div буде видно


312

Я використовую jQuery на своєму веб-сайті, і я хотів би ініціювати певні дії, коли певний div стане видимим.

Чи можна приєднати якийсь "невидимий" обробник подій до довільних div та мати певний код, коли вони стануть видимими?

Я хотів би щось на зразок наступного псевдокоду:

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

Код попередження ("зроби щось") не повинен спрацьовувати, поки contentDiv фактично не стане видимим.

Дякую.


Відповіді:


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

Ви також можете створити інший метод, щоб вам не довелося перекривати оригінальний метод .show ().


7
EDIT: У цього методу є лише один недолік: вам доведеться повторити одне і те ж «розширення» для всіх методів, які розкривають елемент: show (), slideDown () і т. Д. Для вирішення цієї проблеми потрібно раз і більш універсальне все, оскільки неможливо провести "готову" подію для делегата () або в прямому ефірі ().
Шахріяр Іманов

1
Добре, єдина проблема полягає в тому, що fadeToфункція не працює належним чином після реалізації цієї функції
Омід

9
Ваш код не працює з останньою jQuery (1.7.1 на дату цього коментаря). Я переробив це рішення трохи працювати з останнім JQuery: stackoverflow.com/a/9422207/135968
mkmurray

1
Неможливо змусити цей код працювати з видимістю div, викликаною відповіддю ajax.
JackTheKnife

Новачок тут. Я не розумію, чому на Obj (і newCallback) можна посилатися поза декларацією функції (), як це можна побачити в apply (). Мене вчили, що декларування за допомогою var змінює локальні змінні, але видалення "var" робить їх автоматично глобальними.
Yuta73

85

Проблему вирішують спостерігачі мутації 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
Dan Atkinson

добре працює в хромі, але не працює в Blackberry 10 каскадному веб-перегляді (якщо хтось інший дбає;))
Гійом Джендр

1
Схоже, це не спрацьовує, якщо зміна видимості викликана зміною атрибута предка того, кого контролюють.
Майкл

4
Схоже, це не працює в Chrome 51, не знаю, чому. Запуск вищевказаного коду та натискання кнопки, без попередження.
Кріс

76

Немає рідної події, яку ви можете підключити для цього, однак ви можете викликати подію зі свого сценарію після того, як ви зробите видимий поділ за допомогою. тригерна функція

напр

//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
Моє обмеження тут полягає в тому, що я не обов'язково маю доступ до коду, який показує () мою діву. Тому я б не зміг насправді викликати метод тригер ().
frankadelic

11
Спілкування було надано командою з розвитку поза моєю організацією. Це також щось із "чорного поля", тому ми не хочемо змінювати цей код, якщо це можливо. Це може бути наш єдиний вибір.
frankadelic

ви завжди можете натискати на їх js функції за допомогою власної реалізації. Однак звучить похмуро!
переосмислити

11
@redsquare: Що робити, якщо show()викликається з декількох місць, окрім блоку коду, обговореного вище?
Робін Мабен

1
У цьому прикладі ви повинні змінити ім'я функції, onIsVisibleоскільки використання "isVisible" зараз трохи неоднозначне.
Бред Джонсон

27

Ви можете використовувати плагін jQuery Live Query . І написати код наступним чином:

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

Тоді кожного разу, коли видно вмістDiv, буде попереджено "зробіть щось"!


Ну бовтайте, що працює. Я подумав про це і відхилив його, як навряд чи вдасться, не намагаючись. Треба було б спробувати. :)
neminem

1
Не працювало для мене. Я отримую помилку "livequery - це не функція". Спробував і "jquery-1.12.4.min.js", і "jquery-3.1.1.min.js"
Пол Горбас

2
@Paul: Це плагін
Крістіан

Це може значно уповільнити ваш веб-сайт! Плагін livequery здійснює швидке опитування за атрибутами, а не використовує сучасні ефективні методи, такі як спостерігачі мутації DOM. Тому я вважав за краще б рішення @hegemon: stackoverflow.com/a/16462443/19163 (і використання опитування тільки в якості запасного варіанту для старих версій IE) - ВОГ 1 день тому
ВОГ

20

рішення червоного кольору - правильна відповідь.

Але як ІНТЕОРІЙНЕ рішення ви можете написати функцію, яка вибирає елементи, класифіковані за .visibilityCheck( не всі видимі елементи ), і перевіряти їх visibilityвластивість; якщо trueтоді щось робити.

Після цього функцію слід періодично виконувати за допомогою setInterval()функції. Ви можете зупинити таймер за допомогою clearInterval()успішного виклику.

Ось приклад:

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

window.setInterval(foo, 100);

Ви також можете виконати деякі покращення продуктивності, проте рішення в основному є абсурдним для використання в дії. Тому...


5
Недобра форма використовувати функцію, що мається на увазі, всередині setTimeout / setInterval. Використовуйте setTimeout (foo, 100)
redsquare

1
Я мушу передати це вам, це творче, хоч і неприємно. І це було досить швидко і брудно, щоб зробити фокус для мене способом, сумісним з IE8. Дякую!
Дж. Сміт

або 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
Для мене це спрацювало чудово, але важливо зазначити, що функція порушує ланцюжок на показі та приховує функції, що розбиває багато плагінів. Додайте повернення перед, el.apply(this, arguments)щоб виправити це.
jaimerump

Це те, що я шукав! Повернення потрібно додати, як у коментарі від @jaimerump
Brainfeeder

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: видалений перемикач, оскільки він запускає show / hid, і ми не хочемо 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 і deleteAttr теж?
Андрія

5

У мене була ця сама проблема і я створив плагін jQuery, щоб вирішити його для нашого сайту.

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

Ось як би ви його використовували на основі прикладу:

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

1
Опитування не є дуже ефективним і суттєво уповільнить браузер, якби це було використано для багатьох елементів.
Серін

4

Використовуйте шляхові точки jQuery:

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

Інші приклади на сайті jQuery Waypoints .


1
Це працює лише тоді, коли елемент стає видимим через прокручування, а не через інші прогностичні зміни.
AdamJones

@AdamJones Коли я використовую клавіатуру, вона працює як очікувалося. Напр .: imakewebthings.com/jquery-waypoints/shortcuts/infinite-scroll
Федір РИХТИК

4

Що мені тут допомогло, - це нещодавня специфікація поліфункції ResizeObserver :

const divEl = $('#section60');

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

Зауважте, що він є перехресним і виконавчим (без опитування).


Добре працював для виявлення кожного разу, коли рядок таблиці відображався / приховувався на відміну від інших, а також плюс - не було необхідного плагіна!
BrettC

3

Я досяг простої заданої інтервальної функції, щоб досягти цього. Якщо видно елемент з класом 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

1

Для перегляду змін в атрибутах DOM доступний плагін jQuery,

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

Обгортання плагіну Все, що вам потрібно зробити, - це прив’язати MutationObserver

Потім ви можете використовувати його для перегляду діва за допомогою:

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

Я змінив спусковий механізм приховування / показу на Catalint на основі ідеї Гленна. Моя проблема полягала в тому, що у мене є модульний додаток. Я змінююсь між модулями, які показують і ховають діви-батьків. Потім, коли я приховую модуль і показую інший, з його методом у мене помітна затримка, коли я змінююсь між модулями. Мені потрібно лише інколи запалити цю подію і в деяких особливих дітях. Тому я вирішив повідомити лише про дітей із класом "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';
                    }">

Цей код автоматичного керування не вимагає видимого або невидимого керування запитом

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.