¿Detector de eventos para cuando el elemento se vuelve visible?

113

Estoy creando una barra de herramientas que se incluirá en una página. el div en el que se incluirá se mostrará de forma predeterminada : ninguno . ¿Hay alguna manera de poner un detector de eventos en mi barra de herramientas para que escuche cuando se vuelva visible y pueda inicializarse? ¿O tendré que pasarle una variable de la página que lo contiene?

Gracias

JD Isaacks
fuente
¿Ayuda esta respuesta? stackoverflow.com/questions/1397251/…
Kangax
@kangax, gracias. Pero como no está ampliamente implementado, creo que voy a descartar toda la idea del oyente de eventos y tomar una ruta diferente.
JD Isaacks
Consulte esta respuesta para ver una implementación de un evento "onVisible" en JavaScript: stackoverflow.com/a/3807340/975097
Anderson Green
Podría ver esto por favor. stackoverflow.com/questions/45429746/…
Martin

Respuestas:

8

Los eventos de Javascript tratan con la interacción del usuario , si su código está lo suficientemente organizado, debería poder llamar a la función de inicialización en el mismo lugar donde cambia la visibilidad (es decir, no debería cambiar myElement.style.displayen muchos lugares, en su lugar, llame a una función / método que sí esto y cualquier otra cosa que desee).

maltalef
fuente
8
Pero, ¿cómo podría detectar el cambio en la visibilidad (es decir, llamar a una función tan pronto como un elemento se vuelva visible)?
Anderson Green
Creo que no puedes hacer eso. Lo que harías en lugar de intentar detectar el cambio es saber exactamente dónde se provoca y poner tu llamada allí. Si el cambio en la visibilidad no es algo de lo que su propio código sea directamente responsable (por ejemplo, algunas bibliotecas) y la lógica por la que sucede es muy artificial, ¿supongo que no tiene suerte? :)
maltalef
6
Rechacé esta pregunta porque a pesar de todo el sentido que tiene, es irrelevante para el problema real. (si no muestro ninguno en un marco, todos los elementos secundarios se vuelven invisibles)
Sebas
2
@Sebas, realmente no entiendo por qué la visibilidad de los niños es importante aquí. De todos modos, si la respuesta no fue útil para su caso particular (cualquiera que sea) pero apunta a una solución (o una explicación de por qué no hay una solución) para la mayoría de los programadores, sigue siendo una respuesta válida en mi humilde opinión. Si le gusta más una de las otras respuestas (especialmente una que se hizo en un momento posterior, cuando la tecnología mejoró y surgieron nuevas opciones disponibles), creo que una solicitud para cambiar la respuesta correcta sería más apropiada que un voto negativo. En cualquier caso, gracias por el
aviso
2
@figha, no los niños, pero si el elemento que está viendo está en un marco que se muestra = ninguno, no tendrá ningún signo de él.
Sebas
66

En el futuro, la nueva API HTML Intersection Observer es lo que está buscando. Le permite configurar una devolución de llamada que se llama cada vez que un elemento, llamado objetivo, se cruza con la ventana gráfica del dispositivo o con un elemento específico. Está disponible en las últimas versiones de Chrome, Firefox y Edge. Consulte https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API para obtener más información.

Ejemplo de código simple para observar la pantalla: ninguna conmutación:

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

En acción: https://jsfiddle.net/elmarj/u35tez5n/5/

Elmar Jansen
fuente
2
> La API Intersection Observer le permite configurar una devolución de llamada que se llama cada vez que un elemento, llamado objetivo, se cruza con la ventana gráfica del dispositivo o con un elemento especificado;
Stil
1
Tenga en cuenta que InteractionObserver no funciona en Safari en este momento. caniuse.com/#feat=intersectionobserver Parece que hay un parche en curso. bugs.webkit.org/show_bug.cgi?id=159475
Pic Mickael
8
¿Cuándo va a dejar de arruinar nuestras vidas la falta de apoyo de IE para algo tan genial como esto?
Peter Moore
5
@PeterMoore tan pronto como dejes de intentar apoyarlo. Simplemente no lo hagas.
johny why
4
LOL. A veces no es nuestra decisión.
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 });

Puede que llegue un poco tarde, pero podrías usar MutationObserver para observar cualquier cambio en el elemento deseado. Si se produce algún cambio, solo tendrá que comprobar si se muestra el elemento.

usuario3588429
fuente
4
Esto no funciona cuando no se muestra targetNode porque no se muestra un ancestro pero luego se muestra.
Marco Eckstein
¡Voto a favor! Usé esto en otra respuesta de stackoverflow: stackoverflow.com/questions/48792245/…
Nick Timmer
Desafortunadamente, esto solo funciona cuando se usan estilos en línea en el elemento, no cuando el cambio es el resultado del cambio de la clase CSS (que es el escenario más probable, dado que en la situación anterior probablemente ya tenga control programático completo). Violín que muestra el problema: jsfiddle.net/elmarj/uye62Lxc/4 . Vea aquí para una discusión más completa sobre cómo observar los cambios de estilo: dev.to/oleggromov/observing-style-changes---d4f
Elmar Jansen
En realidad, el observador correcto para usar aquí sería un IntersectionObserver- stackoverflow.com/a/52627221/2803743
kano
Esta solución es útil cuando desea activar y desactivar un elemento, y no tiene control directo de ese elemento, y desea reaccionar al cambio del valor de visualización
Eran Goldin
15

Hay al menos una forma, pero no es muy buena. Podrías sondear el elemento en busca de cambios como este:

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

El estándar DOM también especifica eventos de mutación , pero nunca he tenido la oportunidad de usarlos y no estoy seguro de qué tan bien son compatibles. Los usarías así:

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

Este código está fuera de mi cabeza, así que no estoy seguro de si funcionaría.

La mejor y más fácil solución sería tener una devolución de llamada en la función que muestra su objetivo.

slikts
fuente
1
Interesante, gracias. Unos años más tarde, el estado de DOMAttrModified ha cambiado: "Desaprobado. Esta función se ha eliminado de los estándares web. Aunque algunos navegadores pueden seguir admitiéndola, está en proceso de descartarse". (Mozilla)
BurninLeo
MutationObserver es la nueva novedad.
mindplay.dk
14

Tuve este mismo problema y creé un complemento jQuery para resolverlo en nuestro sitio.

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

Así es como lo usaría según su ejemplo:

$('#contentDiv').visibilityChanged(function(element, visible) {
    alert("do something");
});
Shaun Bowe
fuente
4
Esta solución utiliza .is(':visible')junto con setTimeout.
Hp93
7

Como dice @figha, si esta es su propia página web , debe ejecutar lo que necesite ejecutar después de hacer visible el elemento.

Sin embargo, con el fin de responder a la pregunta (y para cualquiera que haga extensiones de Chrome o Firefox , donde este es un caso de uso común), Mutation Summary y Mutation Observer permitirán que los cambios DOM activen eventos.

Por ejemplo, activar un evento para un elemento con un data-widgetatributo que se agrega al DOM. Tomando prestado este excelente ejemplo del blog de David Walsh :

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

Las respuestas incluyen added, removed, valueChangedy mucho más . valueChangedincluye todos los atributos, incluidos displayetc.

mikemaccana
fuente
Probablemente porque quiere saber cuándo está realmente en pantalla. Lo hago, solo puedo asumir que el votante negativo tiene la misma razón.
Dave Hillier
No responde la pregunta y no se recomienda publicar solo enlaces.
Alex Holsgrove
@AlexHolsgrove He agregado un ejemplo, desde el enlace. Dado que el póster habla de agregar una barra de herramientas a la página, parece que podrían estar agregando una barra de herramientas a una página de terceros y esperando que se agregue un elemento al DOM, en cuyo caso Mutations es la mejor API.
Mikemaccana
Tenga en cuenta que esto todavía no cubre la parte que explica en qué parte del proceso de observación de mutaciones entra la "visibilidad".
Mike 'Pomax' Kamermans
3

Solo para comentar sobre el soporte del navegador del detector de eventos DOMAttrModified:

Compatibilidad con varios navegadores

Estos eventos no se implementan de manera uniforme en diferentes navegadores, por ejemplo:

  • IE antes de la versión 9 no admitía los eventos de mutación en absoluto y no implementa algunos de ellos correctamente en la versión 9 (por ejemplo, DOMNodeInserted)

  • WebKit no es compatible con DOMAttrModified (consulte el error 8191 de webkit y la solución)

  • "eventos de nombre de mutación", es decir, DOMElementNameChanged y DOMAttributeNameChanged no son compatibles con Firefox (a partir de la versión 11), y probablemente también en otros navegadores.

Fuente: https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Mutation_events

Josh
fuente
-2

mi solución:

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

por ejemplo:

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

function processMoreLessButton(){
//some logic
}
usuario2699000
fuente
5
Debe decirles a todos que ha utilizado el código de jQuery original, inspiraría más confianza;)
aemonge
1
"mi solución". El descaro de este tipo.
André Silva
@ AndréSilva Quizás escribió jQuery. : O
Andrew