Событие jQuery для запуска действия, когда div становится видимым


312

Я использую jQuery на своем сайте, и я хотел бы вызвать определенные действия, когда определенный div становится видимым.

Можно ли присоединить какой-то «видимый» обработчик событий к произвольным элементам 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();
});

Это эффективно позволяет вам что-то делать beforeShow и afterShow, в то же время выполняя обычное поведение исходного метода .show ().

Вы также можете создать другой метод, чтобы вам не приходилось переопределять исходный метод .show ().


7
РЕДАКТИРОВАТЬ: у этого метода есть только один недостаток: вам придется повторить одно и то же «расширение» для всех методов, которые раскрывают элемент: show (), slideDown () и т. Д. Чтобы решить эту проблему один раз, нужно что-то более универсальное и все, так как невозможно иметь "готовое" событие для делегата () или live ().
— Шахрияр Иманов

1
Хорошо, единственная проблема в том, что fadeToфункция не работает должным образом после реализации этой функции
— Омид

9
Ваш код не работает с последней версией jQuery (1.7.1 на дату этого комментария). Я немного переработал это решение для работы с последней
— версией

1
Не удается заставить этот код работать с видимостью div, вызванной ответом ajax.
— JackTheKnife

Новичок здесь. Я не понимаю, почему на obj (и newCallback) можно ссылаться вне объявления функции (), как это видно в 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
— Дэн Аткинсон,

хорошо работает в Chrome, но не работает в веб-представлении Blackberry 10 cascade (если кому-то еще это интересно;))
— Гийом Жандр

1
Это не работает, если изменение видимости вызвано изменением атрибута в предке отслеживаемого объекта.
— Майкл

4
Это не похоже на работу в Chrome 51, не уверен, почему. Запуск вышеуказанного кода и нажатие кнопки, без предупреждения.
— Крис

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
Мое ограничение здесь заключается в том, что я не обязательно имею доступ к коду, который показывает () это мой div. Поэтому я не смог бы вызвать метод trigger ().
— Франкладик

11
JS был предоставлен командой разработчиков за пределами моей организации. Это также что-то вроде «черного ящика», поэтому мы не хотим изменять этот код, если это возможно. Это может быть наш единственный выбор, хотя.
— frankadelic

вы всегда можете пометить их функции js своей собственной реализацией. Звучит мрачно, однако!
— красный квадрат

11
@redsquare: Что делать, если он show()вызывается из нескольких мест, кроме блока кода, описанного выше?
— Робин Мабен

1
В этом примере вы должны изменить имя функции на, onIsVisibleпотому что использование isVisible сейчас немного неоднозначно.
— Брэд Джонсон

27

Вы можете использовать плагин Live Query от jQuery . И напишите код следующим образом:

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

Тогда каждый раз, когда contentDiv виден, «делать что-то» будет предупреждено!


Ну черт, это работает. Я думал об этом и отверг это как маловероятное, не пытаясь это сделать. Надо было попробовать. :)
— 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

Решение 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. Используйте setTimeout (foo, 100)
— красный квадрат

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
Это отлично сработало для меня, но важно отметить, что функция разбивает цепочки на функции show и hide, что нарушает множество плагинов. Добавьте возврат перед, 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 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;
        }
    });
});

2
Вы также должны проверить attr и removeAttr тоже?
— Андрия

5

У меня была такая же проблема, и я создал плагин jQuery, чтобы решить ее для нашего сайта.

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

Вот как вы могли бы использовать его на основе вашего примера:

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

1
Опрос не очень эффективен и значительно замедлит работу браузера, если он будет использоваться для многих элементов.
— Церин

4

Используйте jQuery Waypoints:

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

Я сделал простую функцию 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

Я изменил триггер скрытия / показа событий от 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;
    }
});

Затем, когда ребенок хочет прослушать событие «show» или «hide», я должен добавить ему класс «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.