Ouvinte de eventos para quando o elemento se torna visível?

113

Estou construindo uma barra de ferramentas que será incluída em uma página. o div no qual será incluído será exibido por padrão : nenhum . Existe uma maneira de colocar um ouvinte de eventos na minha barra de ferramentas para ouvir quando ele se tornar visível para que possa inicializar? ou terei que passar uma variável da página que o contém?

obrigado

JD Isaacks
fonte
Esta resposta ajuda? stackoverflow.com/questions/1397251/…
kangax
@kangax, obrigado. Mas, como não foi amplamente implementado, acho que vou riscar toda a ideia do ouvinte de evento e seguir um caminho diferente.
JD Isaacks
Veja esta resposta para uma implementação de um evento "onVisible" em JavaScript: stackoverflow.com/a/3807340/975097
Anderson Green
possível duplicata de Como implementar um evento 'onVisible' em Javascript?
usuário de
Poderia ver isso, por favor. stackoverflow.com/questions/45429746/…
Martin

Respostas:

8

Os eventos Javascript lidam com a interação do usuário , se o seu código estiver organizado o suficiente, você deve ser capaz de chamar a função de inicialização no mesmo lugar onde a visibilidade muda (ou seja, você não deve mudar myElement.style.displayem muitos lugares, em vez disso, chame uma função / método que o faça isso e qualquer outra coisa que você queira).

maltalef
fonte
8
Mas como você seria capaz de detectar a mudança na visibilidade (ou seja, chamar uma função assim que um elemento se tornar visível)?
Anderson Green,
Eu acho que você não pode fazer isso. O que você faria, em vez de tentar detectar a mudança, é saber exatamente onde ela é provocada e ligar para lá. Se a mudança na visibilidade não é algo pelo qual seu próprio código é diretamente responsável (por exemplo, alguma lib) e a lógica pela qual isso acontece é muito artificial, acho que você está sem sorte? :)
maltalef
6
Eu votei contra esta questão porque, apesar de todo o sentido que faz, é irrelevante para o problema real. (se eu não exibir nenhum em um quadro, todos os elementos filhos tornam-se invisíveis)
Sebas
2
@Sebas, eu realmente não entendo porque a visibilidade das crianças é importante aqui. De qualquer forma, se a resposta não foi útil para seu caso específico (seja qual for), mas aponta para uma solução (ou explicação de por que não existe uma solução) para a maioria dos programadores, ainda é uma resposta válida em minha humilde opinião. Se você gostar mais de uma das outras respostas (especialmente uma que foi feita posteriormente, quando a tecnologia melhorou e novas opções surgiram), acredito que uma solicitação para alterar a resposta correta seria mais apropriada do que um downvote. Em qualquer caso, obrigado pelo
aviso
2
@figha, não os filhos, mas se o elemento que você está assistindo estiver em um quadro que é exibido = nenhum, você não terá nenhum sinal dele.
Sebas
66

No futuro, a nova API HTML Intersection Observer é o que você está procurando. Ele permite que você configure um retorno de chamada que é chamado sempre que um elemento, chamado de destino, cruza a janela de visualização do dispositivo ou um elemento especificado. Ele está disponível nas versões mais recentes do Chrome, Firefox e Edge. Consulte https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API para obter mais informações.

Exemplo de código simples para observação da tela: nenhuma comutação:

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

Em ação: https://jsfiddle.net/elmarj/u35tez5n/5/

Elmar Jansen
fonte
2
> A API Intersection Observer permite configurar um retorno de chamada que é chamado sempre que um elemento, chamado de destino, cruza a janela de visualização do dispositivo ou um elemento especificado;
ainda
1
Observe que o InteractionObserver não funciona no Safari no momento. caniuse.com/#feat=intersectionobserver Parece que há um patch em andamento. bugs.webkit.org/show_bug.cgi?id=159475
Pic Mickael
8
Quando é que a falta de suporte do IE para algo legal assim vai parar de arruinar nossas vidas?
Peter Moore
5
@PeterMoore assim que você parar de tentar apoiá-lo. Apenas não faça isso.
johny porque
4
RI MUITO. Às vezes, não é nossa decisão.
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 });

Posso estar um pouco atrasado, mas você poderia apenas usar o MutationObserver para observar quaisquer alterações no elemento desejado. Se ocorrer alguma alteração, você só terá que verificar se o elemento é exibido.

user3588429
fonte
4
Isso não funciona quando o targetNode não é exibido porque um ancestral não é exibido, mas é exibido.
Marco Eckstein
Votado! Usei isso em outra resposta stackoverflow
Nick Timmer
Infelizmente, isso só funciona ao usar estilos embutidos no elemento, não quando a mudança é resultado da mudança da classe CSS (que é o cenário mais provável, dado que na primeira situação você provavelmente já tem controle programático total). Fiddle que mostra o problema: jsfiddle.net/elmarj/uye62Lxc/4 . Veja aqui uma discussão mais completa sobre como observar as mudanças de estilo: dev.to/oleggromov/observing-style-changes---d4f
Elmar Jansen
Na verdade, o Observador correto a ser usado aqui seria um IntersectionObserver- stackoverflow.com/a/52627221/2803743
kano
Esta solução é útil quando você deseja ativar e desativar um elemento e não tem controle direto desse elemento e deseja reagir à alteração do valor de exibição
Eran Goldin
15

Existe pelo menos um caminho, mas não é muito bom. Você pode apenas pesquisar o elemento para mudanças como esta:

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

O padrão DOM também especifica eventos de mutação , mas nunca tive a chance de usá-los e não tenho certeza se eles são bem suportados. Você os usaria assim:

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

Este código está na minha cabeça, então não tenho certeza se funcionaria.

A melhor e mais fácil solução seria ter um retorno de chamada na função exibindo seu destino.

slikts
fonte
1
Interessante, obrigado. Alguns anos depois, o status de DOMAttrModified mudou: "Obsoleto. Este recurso foi removido dos padrões da Web. Embora alguns navegadores ainda possam suportá-lo, ele está em processo de ser abandonado." (Mozilla)
BurninLeo
MutationObserver é o novo.
mindplay.dk
14

Eu tive esse mesmo problema e criei um plugin jQuery para resolvê-lo em nosso site.

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

Aqui está como você o usaria com base em seu exemplo:

$('#contentDiv').visibilityChanged(function(element, visible) {
    alert("do something");
});
Shaun Bowe
fonte
4
Esta solução usa .is(':visible')junto com setTimeout.
HP93
7

Como @figha diz, se esta é sua própria página da web , você deve apenas executar o que for necessário para executar após tornar o elemento visível.

No entanto, para o propósito de responder à pergunta (e qualquer um que fizer extensões para Chrome ou Firefox , onde este é um caso de uso comum), Mutation Summary e Mutation Observer permitirão que as alterações do DOM acionem eventos.

Por exemplo, disparar um evento para um elemento com data-widgetatributo sendo adicionado ao DOM. Pegando emprestado este excelente exemplo do 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);

As respostas incluem added, removed, valueChangede mais . valueChangedinclui todos os atributos, incluindo displayetc.

mikemaccana
fonte
Provavelmente porque ele quer saber quando está realmente na tela. Sim, só posso assumir que o downvoter tem o mesmo motivo.
Dave Hillier
Não responde à pergunta, e postar apenas links não é recomendado.
Alex Holsgrove
@AlexHolsgrove Eu adicionei um exemplo, do link. Já que o autor fala sobre adicionar uma barra de ferramentas à página, parece que eles podem estar adicionando uma barra de ferramentas a uma página de terceiros e aguardando que um elemento seja adicionado ao DOM, caso em que Mutations é a melhor API.
mikemaccana
Observe que isso ainda não cobre a parte que explica onde a "visibilidade" do processo de observação da mutação entra.
Mike 'Pomax' Kamermans
3

Apenas para comentar sobre o suporte do navegador do listener de eventos DOMAttrModified:

Suporte para vários navegadores

Esses eventos não são implementados de forma consistente em diferentes navegadores, por exemplo:

  • O IE anterior à versão 9 não era compatível com os eventos de mutação e não implementava alguns deles corretamente na versão 9 (por exemplo, DOMNodeInserted)

  • O WebKit não é compatível com DOMAttrModified (consulte o bug 8191 do webkit e a solução alternativa)

  • "eventos de nome de mutação", ou seja, DOMElementNameChanged e DOMAttributeNameChanged não são suportados no Firefox (a partir da versão 11), e provavelmente em outros navegadores também.

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

Josh
fonte
-2

minha solução:

; (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 exemplo:

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

function processMoreLessButton(){
//some logic
}
user2699000
fonte
5
Você deve dizer a todos que usou o código original do jQuery, ele inspiraria mais confiança;)
aemonge
1
"minha solução". A coragem desse cara.
André Silva
@ AndréSilva Talvez ele tenha escrito jQuery. : O
Andrew