Evento jQuery: detecta alterações no html / texto de uma div


265

Eu tenho uma div que tem seu conteúdo mudando o tempo todo, seja ajax requests, jquery functions, bluretc etc.

Existe uma maneira de detectar alterações na minha div a qualquer momento?

Eu não quero usar nenhum intervalo ou valor padrão verificado.

Algo assim faria

$('mydiv').contentchanged() {
 alert('changed')
}


14
@Rob Isso vincula um keypressevento a um <div>elemento editável por conteúdo . Não tenho certeza de que as soluções se aplicam a isso. Definitivamente, eles não captariam nenhuma mudança programática no conteúdo de um elemento.
Anthony Grist 27/03

Respostas:


408

Se você não quiser usar o timer e verificar o innerHTML, tente este evento

$('mydiv').bind('DOMSubtreeModified', function(){
  console.log('changed');
});

Mais detalhes e dados de suporte do navegador estão aqui .

Atenção: nas versões mais recentes do jQuery, bind () está obsoleto, portanto, você deve usar on ():

$('body').on('DOMSubtreeModified', 'mydiv', function(){
  console.log('changed');
});

14
Lembre-se de que DOMSubtreeModified não é suportado no IE8 (e abaixo).
Gavin


3
Mozilla 33: caiu na recursão pelo elemento <body>. Precisava encontrar outra maneira
Chaki_Black 28/10

17
É sério NÃO use esse evento, ele irá travar todo o seu trabalho, pois ele é acionado o tempo todo. Em vez disso, use os eventos abaixo de $ ('. MyDiv'). Bind ('DOMNodeInserted DOMNodeRemoved', function () {});
21816 George SEDRA

19
Este método foi preterido! Em vez disso, use:$("body").on('DOMSubtreeModified', "mydiv", function() { });
Asif

55

Usando Javascript MutationObserver

  //More Details https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
 // select the target node
var target = document.querySelector('mydiv')
// create an observer instance
var observer = new MutationObserver(function(mutations) {
  console.log($('mydiv').text());   
});
// configuration of the observer:
var config = { attributes: true, childList: true, characterData: true };
// pass in the target node, as well as the observer options
observer.observe(target, config);

6
Esta é a resposta correta, pois agora isso é favorecido com o uso do DOMSubtreeModified
Joel Davey

3
Estou recebendo erro com isso, mesmo que eu tenha dado o seletor correto. "VM21504: 819 TypeError não capturado: falha ao executar 'observe' em 'MutationObserver': o parâmetro 1 não é do tipo 'Nó'."
samir 18/07/19


42

Você pode tentar isso

$('.myDiv').bind('DOMNodeInserted DOMNodeRemoved', function() {

});

mas isso pode não funcionar no Internet Explorer, não foi testado



3
NOTA !: se você usar isso como gatilho e houver muitas alterações sendo feitas na página - ela executará sua função X vezes (talvez até X = 1.000 ou mais), o que pode ser muito ineficiente. Uma solução simples é definir uma var booleana "em execução", que irá ... se (executando == true) {return} ... sem executar seu código, se ele já estiver em execução. Defina running = true logo após sua lógica if e running = false antes de sua função sair. Você também pode usar um cronômetro para limitar sua função e poder executar apenas a cada X segundos. running = true; setTimeout (function () {running = false}, 5000); (ou algo melhor)
JxAxMxIxN

Eu usei isso em uma caixa de seleção que tinha opções sendo adicionadas e removidas. Funcionou muito bem quando os itens foram adicionados, mas a remoção parecia ter 1 item atrás. Quando a última opção foi removida, ela não disparava.
precisa saber é o seguinte

2
@JxAxMxIxN Você também pode aumentar o tempo limite limpando e definindo o tempo limite novamente:clearTimeout(window.something); window.something = setTimeout(...);
Ctrl-C

concordou - o seu caminho é o caminho a percorrer - desde aprender Python Eu tenho esclarecido muitas das minhas práticas de codificação pobres em várias línguas (não todos, apenas um monte;)
JxAxMxIxN

33

Você está procurando MutationObserver ou Mutation Events . Nem são suportados em todos os lugares nem são encarados com muito carinho pelo mundo do desenvolvedor.

Se você sabe (e pode ter certeza de que) o tamanho da div será alterado, poderá usar o evento de redimensionamento do navegador cruzado .


1
É esse. Especificamente, DOMSubtreeModified . Você pode achar útil a biblioteca de resumo de mutações e esta lista de eventos na árvore do DOM .
BenjaminRH


9
Caso alguém tenha tentado ler tudo em todos os lugares, esta é a resposta correta. Eventos de mutação foram suportados em navegadores anteriores, o Mutation Observer é o que é suportado em navegadores modernos e será suportado no futuro. Veja o link para suporte: CANIUSE Mutation Observer
Josh Mc

20

O código a seguir funciona para mim.

$("body").on('DOMSubtreeModified', "mydiv", function() {
    alert('changed');
});

Espero que ajude alguém :)


Esta é a mesma resposta que a de @Artley
Black

@Black Thankyou! Acabei de verificar a resposta Artley. Eu cuidarei disso da próxima vez.
Sanchit Gupta

17

Não há solução embutida para esse problema, este é um problema com seu padrão de design e codificação.

Você pode usar o padrão de editor / assinante. Para isso, você pode usar eventos personalizados do jQuery ou seu próprio mecanismo de eventos.

Primeiro,

function changeHtml(selector, html) {
    var elem = $(selector);
    jQuery.event.trigger('htmlchanging', { elements: elem, content: { current: elem.html(), pending: html} });
    elem.html(html);
    jQuery.event.trigger('htmlchanged', { elements: elem, content: html });
}

Agora você pode assinar os eventos divhtmlchanging / divhtmlchanged da seguinte maneira,

$(document).bind('htmlchanging', function (e, data) {
    //your before changing html, logic goes here
});

$(document).bind('htmlchanged', function (e, data) {
    //your after changed html, logic goes here
});

Agora, você deve alterar as alterações no seu conteúdo div através desta changeHtml()função. Portanto, você pode monitorar ou fazer as alterações necessárias adequadamente, porque o argumento de ligação de dados de retorno de chamada que contém as informações.

Você precisa alterar o html da sua div assim;

changeHtml('#mydiv', '<p>test content</p>');

E também, você pode usar isso para qualquer elemento html, exceto o elemento de entrada. De qualquer forma, você pode modificar isso para usar com qualquer elemento (s).


Para observar e agir sobre alterações em um elemento específico, modifique a função changeHtml para usar 'elem.trigger (...)' em vez de 'jQuery.event.trigger (...)' e, em seguida, vincule-o ao elemento como . $ ( '# my_element_id') em ( 'htmlchanged', function (e, de dados) {...}
KenB

8
"este é um problema com seu design e padrão de codificação", o que fazer se você incluir scripts de terceiros, portanto, você não tem controle sobre o código fonte deles? mas você precisa detectar as alterações deles em uma div?
precisa saber é o seguinte

@DrLightman uma regra de ouro é escolher lib terceiro com o evento de retorno de chamada fornecido
Marcel Djaman

8

Use MutationObserver como visto neste snippet fornecido pela Mozilla e adaptado desta postagem do blog

Como alternativa, você pode usar o exemplo JQuery visto neste link

Chrome 18+, Firefox 14+, IE 11+, Safari 6+

// Select the node that will be observed for mutations
var targetNode = document.getElementById('some-id');

// Options for the observer (which mutations to observe)
var config = { attributes: true, childList: true };

// Callback function to execute when mutations are observed
var callback = function(mutationsList) {
    for(var mutation of mutationsList) {
        if (mutation.type == 'childList') {
            console.log('A child node has been added or removed.');
        }
        else if (mutation.type == 'attributes') {
            console.log('The ' + mutation.attributeName + ' attribute was modified.');
        }
    }
};

// Create an observer instance linked to the callback function
var observer = new MutationObserver(callback);

// Start observing the target node for configured mutations
observer.observe(targetNode, config);

// Later, you can stop observing
observer.disconnect();

3

Você pode armazenar o innerHTML antigo da div em uma variável. Defina um intervalo para verificar se o conteúdo antigo corresponde ao conteúdo atual. Quando isso não for verdade, faça alguma coisa.


1

Experimente o MutationObserver:

suporte ao navegador: http://caniuse.com/#feat=mutationobserver

<html>
  <!-- example from Microsoft https://developer.microsoft.com/en-us/microsoft-edge/platform/documentation/dev-guide/dom/mutation-observers/ -->

  <head>
    </head>
  <body>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
    <script type="text/javascript">
      // Inspect the array of MutationRecord objects to identify the nature of the change
function mutationObjectCallback(mutationRecordsList) {
  console.log("mutationObjectCallback invoked.");

  mutationRecordsList.forEach(function(mutationRecord) {
    console.log("Type of mutation: " + mutationRecord.type);
    if ("attributes" === mutationRecord.type) {
      console.log("Old attribute value: " + mutationRecord.oldValue);
    }
  });
}
      
// Create an observer object and assign a callback function
var observerObject = new MutationObserver(mutationObjectCallback);

      // the target to watch, this could be #yourUniqueDiv 
      // we use the body to watch for changes
var targetObject = document.body; 
      
// Register the target node to observe and specify which DOM changes to watch
      
      
observerObject.observe(targetObject, { 
  attributes: true,
  attributeFilter: ["id", "dir"],
  attributeOldValue: true,
  childList: true
});

// This will invoke the mutationObjectCallback function (but only after all script in this
// scope has run). For now, it simply queues a MutationRecord object with the change information
targetObject.appendChild(document.createElement('div'));

// Now a second MutationRecord object will be added, this time for an attribute change
targetObject.dir = 'rtl';


      </script>
    </body>
  </html>


0

A adição de algum conteúdo a uma div, seja através do jQuery ou diretamente da API do DOM, é padronizada para a .appendChild()função O que você pode fazer é substituir a .appendChild()função do objeto atual e implementar um observador nele. Agora, substituindo nossa .appendChild()função, precisamos emprestar essa função de outro objeto para poder acrescentar o conteúdo. Por isso, chamamos o .appendChild()de uma outra div para finalmente anexar o conteúdo. Claro, isso também vale para o .removeChild().

var obj = document.getElementById("mydiv");
    obj.appendChild = function(node) {
        alert("changed!");

        // call the .appendChild() function of some other div
        // and pass the current (this) to let the function affect it.
        document.createElement("div").appendChild.call(this, node);
        }
    };

Aqui você pode encontrar um exemplo ingênuo. Você pode estendê-lo sozinho, eu acho. http://jsfiddle.net/RKLmA/31/

A propósito: isso mostra que o JavaScript está em conformidade com o princípio do OpenClosed. :)


Ele não funciona com anexar filho ... Na verdade, eu modifico o html dele através de outras funções.
BoqBoq

Como removeChild () replaceChild () etc. Mas você está certo no innerHTML. Você deve evitá-lo de alguma forma.
Andries
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.