Слушатель событий, когда элемент становится видимым?

116

Я создаю панель инструментов, которая будет включена в страницу. div, в который он будет включен, по умолчанию будет отображать: none . Есть ли способ разместить прослушиватель событий на моей панели инструментов, чтобы отслеживать, когда он становится видимым, чтобы его можно было инициализировать? или мне придется передать ему переменную с содержащей его страницы?

Спасибо

Джей Ди Айзекс
источник
Этот ответ помогает? stackoverflow.com/questions/1397251/…
kangax
@kangax, спасибо. Но поскольку он широко не реализован, я думаю, что собираюсь отбросить всю идею прослушивателя событий и пойти другим путем.
JD Isaacks
См. Этот ответ для реализации события «onVisible» в JavaScript: stackoverflow.com/a/3807340/975097
Андерсон Грин
возможный дубликат Как реализовать событие onVisible в Javascript?
пользователь
Пожалуйста, посмотрите на это. stackoverflow.com/questions/45429746/…
Мартин

Ответы:

8

События Javascript имеют дело с взаимодействием с пользователем , если ваш код достаточно организован, вы должны иметь возможность вызывать функцию инициализации в том же месте, где изменяется видимость (т.е. вы не должны изменять myElement.style.displayво многих местах, вместо этого вызывайте функцию / метод, это и все, что вам может понадобиться).

мальталеф
источник
8
Но как вы могли бы обнаружить изменение видимости (т.е. вызвать функцию, как только элемент станет видимым)?
Андерсон Грин
Я думаю, ты не можешь этого сделать. Что бы вы сделали вместо того, чтобы пытаться обнаружить изменение, - это точно знать, где оно спровоцировано, и направить туда свой призыв. Если изменение видимости не является тем, за что непосредственно отвечает ваш собственный код (например, какая-то библиотека), и логика, с помощью которой это происходит, очень надумана, я думаю, вам не повезло? :)
maltalef 03
6
Я отклонил этот вопрос, потому что, несмотря на весь его смысл, он не имеет отношения к реальной проблеме. (если я не показываю ничего в кадре, все дочерние элементы становятся невидимыми)
Себас
2
@ Себас, я не совсем понимаю, почему здесь важна видимость детей. В любом случае, если ответ не был полезен для вас в конкретном случае (каким бы он ни был), но указывает на решение (или объяснение, почему решения нет) для большинства программистов, по моему скромному мнению, это все же верный ответ. Если вам больше нравится один из других ответов (особенно тот, который был сделан позже, когда технологии улучшились и стали доступны новые параметры), я считаю, что запрос на изменение правильного ответа будет более подходящим, чем голос против. В любом случае, спасибо за хедз-ап :)
maltalef
2
@figha, а не дочерние элементы, но если элемент, который вы смотрите, находится в кадре, который отображается = none, у вас не будет никаких признаков этого.
Себас
67

Забегая вперед, вам нужен новый HTML Intersection Observer API. Он позволяет вам настроить обратный вызов, который вызывается всякий раз, когда один элемент, называемый целью, пересекает либо область просмотра устройства, либо указанный элемент. Он доступен в последних версиях Chrome, Firefox и Edge. См. Https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API для получения дополнительной информации.

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

// Start observing visbility of element. On change, the
//   the callback is called with Boolean visibility as
//   argument:

respondToVisibility(element, callback) {
  var options = {
    root: document.documentElement
  }

  var observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
      callback(entry.intersectionRatio > 0);
    });
  }, options);

  observer.observe(element);
}

В действии: https://jsfiddle.net/elmarj/u35tez5n/5/

Эльмар Янсен
источник
2
> Intersection Observer API позволяет вам настроить обратный вызов, который вызывается всякий раз, когда один элемент, называемый целью, пересекает либо область просмотра устройства, либо указанный элемент;
еще
1
Обратите внимание, что InteractionObserver в данный момент не работает в Safari. caniuse.com/#feat=intersectionobserver Похоже, идет работа над исправлением. bugs.webkit.org/show_bug.cgi?id=159475
Пик Микаэль,
8
Когда отсутствие поддержки IE для чего-то такого классного перестанет разрушать нашу жизнь?
Питер Мур
5
@PeterMoore, как только ты перестанешь его поддерживать. Только не надо.
johny why
4
РЖУНИМАГУ. Иногда это не наше решение.
Питер Мур
45
var targetNode = document.getElementById('elementId');
var observer = new MutationObserver(function(){
    if(targetNode.style.display != 'none'){
        // doSomething
    }
});
observer.observe(targetNode, { attributes: true, childList: true });

Я могу немного опоздать, но вы можете просто использовать MutationObserver, чтобы наблюдать за любыми изменениями в желаемом элементе. Если произойдет какое-либо изменение, вам просто нужно будет проверить, отображается ли элемент.

user3588429
источник
4
Это не работает, когда targetNode не отображается, потому что предок не отображается, но затем отображается.
Марко Экштейн
Проголосовали! Я использовал это в другом ответе stackoverflow: stackoverflow.com/questions/48792245/…
Ник Тиммер
К сожалению, это работает только при использовании встроенных стилей в элементе, а не тогда, когда изменение является результатом изменения CSS-класса (что является более вероятным сценарием, учитывая, что в первой ситуации у вас, вероятно, уже есть полный программный контроль). Скрипка, которая показывает проблему: jsfiddle.net/elmarj/uye62Lxc/4 . См. Здесь для более полного обсуждения того, как наблюдать за изменениями стиля: dev.to/oleggromov/observing-style-changes---d4f
Эльмар Янсен
На самом деле правильный наблюдатель для использования здесь будет IntersectionObserver- stackoverflow.com/a/52627221/2803743
kano
Это решение полезно, когда вы хотите включать и выключать элемент, но не имеете прямого контроля над этим элементом и хотите реагировать на изменение отображаемого значения
Эран Голдин,
15

Есть хоть один способ, но он не очень удачный. Вы можете просто опросить элемент на предмет таких изменений:

var previous_style,
    poll = window.setInterval(function()
{
    var current_style = document.getElementById('target').style.display;
    if (previous_style != current_style) {
        alert('style changed');
        window.clearInterval(poll);
    } else {
        previous_style = current_style;
    }
}, 100);

Стандарт DOM также определяет события мутации , но у меня никогда не было возможности их использовать, и я не уверен, насколько хорошо они поддерживаются. Вы бы использовали их так:

target.addEventListener('DOMAttrModified', function()
{
    if (e.attrName == 'style') {
        alert('style changed');
    }
}, false);

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

Лучшее и самое простое решение будет иметь функцию обратного вызова в функции отображаются ваши цели.

Slikts
источник
1
Интересно, спасибо. Несколько лет спустя статус DOMAttrModified изменился: «Устарело. Эта функция была удалена из веб-стандартов. Хотя некоторые браузеры все еще поддерживают ее, она находится в процессе отказа». (Mozilla)
BurninLeo
MutationObserver - это новая новинка.
mindplay.dk
14

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

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

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

$('#contentDiv').visibilityChanged(function(element, visible) {
    alert("do something");
});
Шон Боу
источник
4
Это решение используется .is(':visible')вместе с setTimeout.
Hp93
7

Как говорит @figha, если это ваша собственная веб-страница , вы должны просто запустить все, что вам нужно, после того, как вы сделаете элемент видимым.

Однако в целях ответа на вопрос (и для всех, кто создает расширения Chrome или Firefox , где это является обычным вариантом использования), Mutation Summary и Mutation Observer позволят изменениям DOM запускать события.

Например, запуск события для элемента с data-widgetдобавленным атрибутом в DOM. Заимствуя этот отличный пример из блога Дэвида Уолша :

var observer = new MutationObserver(function(mutations) {
    // For the sake of...observation...let's output the mutation to console to see how this all works
    mutations.forEach(function(mutation) {
        console.log(mutation.type);
    });    
});

// Notify me of everything!
var observerConfig = {
    attributes: true, 
    childList: true, 
    characterData: true 
};

// Node, config
// In this case we'll listen to all changes to body and child nodes
var targetNode = document.body;
observer.observe(targetNode, observerConfig);

Ответы включают added, removed, valueChangedи многие другие . valueChangedвключает все атрибуты, в том числе displayи т. д.

микемаккана
источник
Вероятно, потому что он хочет знать, когда это на самом деле на экране. Я верю, я могу только предположить, что у того, кто проголосовал против, та же причина.
Дэйв Хиллиер
Не отвечает на вопрос, и не рекомендуется размещать только ссылки.
Alex Holsgrove
@AlexHolsgrove Я добавил пример по ссылке. Поскольку на плакате говорится о добавлении панели инструментов на страницу, похоже, что они могут добавлять панель инструментов на стороннюю страницу и ждать, пока элемент будет добавлен в DOM, и в этом случае Мутации - лучший API.
mikemaccana
Обратите внимание, что это все еще не охватывает ту часть, которая объясняет, где в процессе наблюдения за мутациями появляется «видимость».
Майк 'Pomax' Камерманс
3

Просто чтобы прокомментировать поддержку браузера прослушивателя событий DOMAttrModified:

Кроссбраузерная поддержка

Эти события не реализуются последовательно в разных браузерах, например:

  • IE до версии 9 вообще не поддерживал события мутации и не реализует некоторые из них правильно в версии 9 (например, DOMNodeInserted)

  • WebKit не поддерживает DOMAttrModified (см. Ошибку webkit 8191 и обходной путь)

  • «события имени мутации», то есть DOMElementNameChanged и DOMAttributeNameChanged, не поддерживаются в Firefox (начиная с версии 11) и, вероятно, в других браузерах.

Источник: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Джош
источник
-2

мое решение:

; (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
}
пользователь2699000
источник
5
Вы должны сказать всем, что использовали исходный код jQuery, это внушит больше уверенности;)
aemonge
1
«мое решение». Нервы у этого парня.
Андре Сильва
@ AndréSilva Может быть, он написал jQuery. : O
Эндрю