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')
}
BoqBoq
fonte
5
Possível duplicata: stackoverflow.com/questions/10328102/…
Rob
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');
});
iman
fonte
14
Lembre-se de que DOMSubtreeModified não é suportado no IE8 (e abaixo).
Gavin
56
este evento foi descontinuado w3.org/TR/DOM-Level-3-Events/#glossary-deprecated
Isaiyavan Babu Karan
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);
PPB
fonte
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
54

Como $("#selector").bind()está obsoleto , você deve usar:

$("body").on('DOMSubtreeModified', "#selector", function() {
    // code here
});
Petar Nikov
fonte
1
Esse foi o fator de diferenciação, o adicional #para indicar que o símbolo deve ir antes do seletor.
Chris - Jr
42

Você pode tentar isso

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

});

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

user1790464
fonte
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 .

rodneyrehm
fonte
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
1
este evento foi descontinuado developer.mozilla.org/pt-BR/docs/Web/Guide/Events/…
Adrien Be
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 :)

Sanchit Gupta
fonte
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).

Sameera Millavithanachchi
fonte
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();
Anthony Awuley
fonte
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.

codelove
fonte
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>

juFo
fonte
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. :)

Andries
fonte
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