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

312

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

Можно ли присоединить какой-то «видимый» обработчик событий к произвольным элементам div и запускать определенный код, когда элемент div становится видимым?

Я хотел бы что-то вроде следующего псевдокода:

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

Код предупреждения («сделать что-то») не должен запускаться до тех пор, пока contentDiv фактически не станет видимым.

Спасибо.

frankadelic
источник
Смотрите здесь: stackoverflow.com/questions/1462138/…
Андерсон Грин,

Ответы:

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

Tres
источник
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 виден, «делать что-то» будет предупреждено!

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

Вы также можете выполнить некоторые улучшения производительности, однако решение в принципе абсурдно использовать в действии. Так...

Sepehr
источник
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);
JellicleCat
источник
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;
        }
    });
});
catalint
источник
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);
Adi.P
источник
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/

MSS
источник
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
});
tlogbon
источник
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);
},

Я хочу помочь

ccsakuweb
источник
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
}
user2699000
источник
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';
                    }">

Этот код автоматического управления не требуется запрос видимого или невидимого элемента управления

ALPC
источник