Odbiornik zdarzeń, kiedy element staje się widoczny?


115

Buduję pasek narzędzi, który zostanie dołączony do strony. div, w którym ma zostać umieszczony, domyślnie wyświetli: none . Czy istnieje sposób, w jaki mogę umieścić detektor zdarzeń na pasku narzędzi, aby nasłuchiwał, kiedy stanie się widoczny, aby można go było zainicjować? czy będę musiał przekazać mu zmienną ze strony zawierającej?

Dzięki



@kangax, dziękuję. Ale ponieważ nie jest szeroko stosowany, myślę, że mam zamiar zarysować cały pomysł słuchacza wydarzenia i pójść inną drogą.
JD Isaacks

Zobacz tę odpowiedź na temat implementacji zdarzenia „onVisible” w JavaScript: stackoverflow.com/a/3807340/975097
Anderson Green


Mogę to zobaczyć, proszę. stackoverflow.com/questions/45429746/…
Martin

Odpowiedzi:


8

Zdarzenia JavaScript dotyczą interakcji użytkownika , jeśli twój kod jest wystarczająco zorganizowany, powinieneś być w stanie wywołać funkcję inicjalizującą w tym samym miejscu, w którym zmienia się widoczność (tj. Nie powinieneś zmieniać myElement.style.displayw wielu miejscach, zamiast tego wywołaj funkcję / metodę, która to robi to i wszystko, czego możesz chcieć).


8
Ale w jaki sposób mógłbyś wykryć zmianę widoczności (tj. Wywołać funkcję, gdy tylko element stanie się widoczny)?
Anderson Green,

Myślę, że nie możesz tego zrobić. Zamiast próbować wykryć zmianę, wiedziałabyś dokładnie, gdzie jest sprowokowana, i umieściłabyś tam swój telefon. Jeśli zmiana widoczności nie jest czymś, za co bezpośrednio odpowiada twój własny kod (np. Niektóre biblioteki), a logika, według której to się dzieje, jest bardzo wymyślona, ​​to chyba nie masz szczęścia? :)
maltalef

6
Odrzuciłem to pytanie, ponieważ pomimo całego sensu, jakie ma, nie ma ono związku z faktycznym problemem. (jeśli nie wyświetlę żadnego na ramce, wszystkie elementy podrzędne stają się niewidoczne)
Sebas

2
@Sebas, naprawdę nie rozumiem, dlaczego widoczność dzieci ma tutaj znaczenie. W każdym razie, jeśli odpowiedź nie była dla Ciebie przydatna w konkretnym przypadku (cokolwiek by to nie było), ale wskazuje na rozwiązanie (lub wyjaśnienie, dlaczego nie ma rozwiązania) dla większości programistów, nadal jest to ważna odpowiedź, moim skromnym zdaniem. Jeśli podoba ci się jedna z pozostałych odpowiedzi (szczególnie ta, która została udzielona później, kiedy udoskonalono technologię i pojawiły się nowe opcje), uważam, że prośba o zmianę poprawnej odpowiedzi byłaby bardziej odpowiednia niż głos przeciw. W każdym razie dzięki za
ostrzeżenie

2
@figha, nie dzieci, ale jeśli oglądany element znajduje się w ramce, która jest wyświetlana = none, nie będzie to żadnego znaku.
Sebas

67

Idąc dalej, nowy interfejs HTML Intersection Observer API jest tym, czego szukasz. Umożliwia skonfigurowanie wywołania zwrotnego, które jest wywoływane za każdym razem, gdy jeden element, zwany celem, przecina widok urządzenia lub określony element. Jest dostępny w najnowszych wersjach Chrome, Firefox i Edge. Więcej informacji można znaleźć pod adresem https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API .

Prosty przykład kodu do obserwowania wyświetlacza: brak przełączania:

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

W akcji: https://jsfiddle.net/elmarj/u35tez5n/5/


2
> Interfejs API Intersection Observer umożliwia skonfigurowanie wywołania zwrotnego, które jest wywoływane za każdym razem, gdy jeden element, nazywany celem, przecina widok urządzenia lub określony element;
nadal

1
Zwróć uwagę, że InteractionObserver nie działa w tej chwili w Safari. caniuse.com/#feat=intersectionobserver Wygląda na to, że trwa aktualizacja. bugs.webkit.org/show_bug.cgi?id=159475
Pic Mickael

8
Kiedy brak wsparcia IE dla czegokolwiek tak fajnego przestanie rujnować nam życie?
Peter Moore

5
@PeterMoore, gdy tylko przestaniesz go wspierać. Po prostu nie.
johny why

4
LOL. Czasami to nie nasza decyzja.
Peter Moore,

45
var targetNode = document.getElementById('elementId');
var observer = new MutationObserver(function(){
    if(targetNode.style.display != 'none'){
        // doSomething
    }
});
observer.observe(targetNode, { attributes: true, childList: true });

Mogę się trochę spóźnić, ale możesz po prostu użyć MutationObserver, aby zaobserwować zmiany w wybranym elemencie. Jeśli nastąpi jakakolwiek zmiana, wystarczy sprawdzić, czy element jest wyświetlany.


4
To nie działa, gdy targetNode nie jest wyświetlany, ponieważ przodek nie jest wyświetlany, ale zostaje wyświetlony.
Marco Eckstein

Głosowano! Użyłem tego w innej odpowiedzi stackoverflow: stackoverflow.com/questions/48792245/ ...
Nick Timmer

Niestety działa to tylko wtedy, gdy używasz stylów wbudowanych w elemencie, a nie gdy zmiana jest wynikiem zmiany klasy CSS (co jest bardziej prawdopodobnym scenariuszem, biorąc pod uwagę, że w poprzedniej sytuacji prawdopodobnie masz już pełną kontrolę programową). Skrzypce, które pokazują problem: jsfiddle.net/elmarj/uye62Lxc/4 . Zobacz tutaj, aby uzyskać pełniejszą dyskusję na temat obserwowania zmian stylu: dev.to/oleggromov/observing-style-changes---d4f
Elmar Jansen

Właściwie prawidłowy Observer do użycia tutaj to IntersectionObserver- stackoverflow.com/a/52627221/2803743
kano

To rozwiązanie jest przydatne, gdy chcesz włączać i wyłączać element i nie masz bezpośredniej kontroli nad tym elementem i chcesz reagować na zmianę wyświetlanej wartości
Eran Goldin

15

Jest co najmniej jeden sposób, ale nie jest on dobry. Możesz po prostu sondować element pod kątem takich zmian:

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

Standard DOM określa również zdarzenia mutacji , ale nigdy nie miałem okazji ich używać i nie jestem pewien, jak dobrze są obsługiwane. Używałbyś ich w ten sposób:

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

Ten kod nie przychodzi mi do głowy, więc nie jestem pewien, czy zadziała.

Najlepszym i najprostszym rozwiązaniem byłoby mieć wywołania zwrotnego w funkcji wyświetlając swój cel.


1
Ciekawe, dziękuję. Kilka lat później stan DOMAttrModified uległ zmianie: „Wycofana. Ta funkcja została usunięta ze standardów internetowych. Chociaż niektóre przeglądarki nadal mogą ją obsługiwać, jest w trakcie usuwania”. (Mozilla)
BurninLeo

MutationObserver to nowa nowość.
mindplay.dk


7

Jak mówi @figha, jeśli jest to Twoja własna strona internetowa , powinieneś po prostu uruchomić wszystko, czego potrzebujesz , po tym , jak sprawisz, że element będzie widoczny.

Jednak w celu udzielenia odpowiedzi na to pytanie (i każdemu, kto tworzy rozszerzenia Chrome lub Firefox , gdzie jest to typowy przypadek użycia), Podsumowanie mutacji i Obserwator mutacji pozwolą zmianom DOM na wyzwalanie zdarzeń.

Na przykład wyzwolenie zdarzenia dla elementu z data-widgetatrybutem dodawanym do DOM. Pożyczając ten doskonały przykład z bloga Davida Walsha :

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

Odpowiedzi m.in. added, removed, valueChangedi więcej . valueChangedobejmuje wszystkie atrybuty, w tym displayitp.


Prawdopodobnie dlatego, że chce wiedzieć, kiedy faktycznie jest na ekranie. Tak, mogę tylko założyć, że przeciwnik ma ten sam powód.
Dave Hillier

Nie odpowiada na pytanie, a publikowanie samych linków nie jest zalecane.
Alex Holsgrove

@AlexHolsgrove Dodałem przykład z linku. Ponieważ plakat mówi o dodaniu paska narzędzi do strony, wydaje się, że mogą dodawać pasek narzędzi do strony trzeciej i czekać na dodanie elementu do DOM, w którym to przypadku Mutations jest najlepszym API.
mikemaccana

Zauważ, że to wciąż nie obejmuje tej części, która wyjaśnia, gdzie w procesie obserwacji mutacji pojawia się „widoczność”.
Mike 'Pomax' Kamermans


3

Wystarczy skomentować obsługę przeglądarki nasłuchiwania zdarzeń DOMAttrModified:

Obsługa wielu przeglądarek

Te zdarzenia nie są konsekwentnie implementowane w różnych przeglądarkach, na przykład:

  • IE przed wersją 9 w ogóle nie obsługiwał zdarzeń mutacji i nie implementuje poprawnie niektórych z nich w wersji 9 (na przykład DOMNodeInserted)

  • WebKit nie obsługuje DOMAttrModified (zobacz błąd 8191 w webkicie i obejście)

  • „Zdarzenia nazw mutacji”, tj. DOMElementNameChanged i DOMAttributeNameChanged nie są obsługiwane w przeglądarce Firefox (od wersji 11) i prawdopodobnie również w innych przeglądarkach.

Źródło: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events


-2

moje rozwiązanie:

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

na przykład:

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

function processMoreLessButton(){
//some logic
}

5
Powinieneś powiedzieć wszystkim, że użyłeś oryginalnego kodu jQuery, wzbudziłoby to większe zaufanie;)
aemonge

1
„moje rozwiązanie”. Zdenerwowanie tego faceta.
André Silva

@ AndréSilva Może napisał jQuery. : O
Andrew
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.