Respostas:
Eu sugiro ouvintes associados aos principais eventos disparados pelo elemento editável, mas você precisa estar ciente de que keydowne keypresseventos são disparados antes que o próprio conteúdo é alterado. Isso não abrangerá todos os meios possíveis de alterar o conteúdo: o usuário também pode usar recortar, copiar e colar nos menus Editar ou navegador de contexto, portanto, você também pode manipular os eventos cut copye paste. Além disso, o usuário pode soltar texto ou outro conteúdo, para que haja mais eventos ( mouseuppor exemplo). Você pode pesquisar o conteúdo do elemento como um substituto.
ATUALIZAÇÃO 29 de outubro de 2014
O evento HTML5input é a resposta a longo prazo. No momento da redação deste artigo, ele é suportado para contenteditableelementos nos navegadores atuais Mozilla (do Firefox 14) e WebKit / Blink, mas não no IE.
Demo:
document.getElementById("editor").addEventListener("input", function() {
console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>
cut, copye pasteeventos em navegadores que os apoiam (IE 5+, o Firefox 3.0+, Safari 3+, Chrome)
inputevento HTML5 .
Aqui está uma versão mais eficiente que é usada onpara todos os conteúdos editáveis. É baseado nas principais respostas aqui.
$('body').on('focus', '[contenteditable]', function() {
const $this = $(this);
$this.data('before', $this.html());
}).on('blur keyup paste input', '[contenteditable]', function() {
const $this = $(this);
if ($this.data('before') !== $this.html()) {
$this.data('before', $this.html());
$this.trigger('change');
}
});
O projeto está aqui: https://github.com/balupton/html5edit
document.getElementById('editor_input').innerHTML = 'ssss'. Desvantagem é que ele requer IE11
Considere usar MutationObserver . Esses observadores são projetados para reagir a alterações no DOM e como substituto de desempenho para eventos de mutação .
Prós:
Contras:
Saber mais:
inputevento HTML5 (que é suportado contenteditableem todos os navegadores WebKit e Mozilla que oferecem suporte a observadores de mutação), já que a mutação DOM pode ocorrer por script e por entrada do usuário, mas é uma solução viável para esses navegadores. Eu imagino que isso poderia prejudicar o desempenho mais do que o inputevento também, mas não tenho provas concretas disso.
inputevento HTML5 é acionado em cada uma dessas situações (especificamente arraste e solte, itálico, copie / recorte / cole no menu de contexto). Também testei negrito c / cmd / ctrl + be obtive os resultados esperados. Também verifiquei e pude verificar se o inputevento não dispara em alterações programáticas (o que parece óbvio, mas é indiscutivelmente contrário a alguma linguagem confusa na página MDN relevante; consulte a parte inferior da página aqui para ver o que quero dizer: desenvolvedor .mozilla.org / pt-
Eu modifiquei a resposta de lawwantsin assim e isso funciona para mim. Eu uso o evento keyup em vez de pressionar a tecla, o que funciona muito bem.
$('#editor').on('focus', function() {
before = $(this).html();
}).on('blur keyup paste', function() {
if (before != $(this).html()) { $(this).trigger('change'); }
});
$('#editor').on('change', function() {alert('changed')});
resposta rápida e suja não jQuery :
function setChangeListener (div, listener) {
div.addEventListener("blur", listener);
div.addEventListener("keyup", listener);
div.addEventListener("paste", listener);
div.addEventListener("copy", listener);
div.addEventListener("cut", listener);
div.addEventListener("delete", listener);
div.addEventListener("mouseup", listener);
}
var div = document.querySelector("someDiv");
setChangeListener(div, function(event){
console.log(event);
});
Duas opções:
1) Para navegadores modernos (sempre-verdes): o evento "input" atuaria como um evento "change" alternativo.
https://developer.mozilla.org/en-US/docs/Web/Events/input
document.querySelector('div').addEventListener('input', (e) => {
// Do something with the "change"-like event
});
ou
<div oninput="someFunc(event)"></div>
ou (com jQuery)
$('div').on('click', function(e) {
// Do something with the "change"-like event
});
2) Para considerar os navegadores IE11 e modernos (sempre-verdes): isso observa as alterações dos elementos e seu conteúdo dentro da div.
https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
var div = document.querySelector('div');
var divMO = new window.MutationObserver(function(e) {
// Do something on change
});
divMO.observe(div, { childList: true, subtree: true, characterData: true });
const p = document.querySelector('p')
const result = document.querySelector('div')
const observer = new MutationObserver((mutationRecords) => {
result.textContent = mutationRecords[0].target.data
// result.textContent = p.textContent
})
observer.observe(p, {
characterData: true,
subtree: true,
})
<p contenteditable>abc</p>
<div />
Aqui está o que funcionou para mim:
var clicked = {}
$("[contenteditable='true']").each(function(){
var id = $(this).attr("id");
$(this).bind('focus', function() {
// store the original value of element first time it gets focus
if(!(id in clicked)){
clicked[id] = $(this).html()
}
});
});
// then once the user clicks on save
$("#save").click(function(){
for(var id in clicked){
var original = clicked[id];
var current = $("#"+id).html();
// check if value changed
if(original != current) save(id,current);
}
});
Esse tópico foi muito útil enquanto eu investigava o assunto.
Modifiquei parte do código disponível aqui em um plug-in jQuery para que ele seja reutilizável, principalmente para satisfazer minhas necessidades, mas outros podem apreciar uma interface mais simples para iniciar o uso inicial de tags editáveis por conteúdo.
https://gist.github.com/3410122
Devido à sua crescente popularidade, o plugin foi adotado por Makesites.org
O desenvolvimento continuará daqui:
Aqui está a solução que acabei usando e funciona fabulosamente. Eu uso $ (this) .text () porque estou apenas usando uma div de uma linha com conteúdo editável. Mas você também pode usar .html () dessa maneira, não precisa se preocupar com o escopo de uma variável global / não global e o anterior é realmente anexado ao editor div.
$('body').delegate('#editor', 'focus', function(){
$(this).data('before', $(this).html());
});
$('#client_tasks').delegate('.task_text', 'blur', function(){
if($(this).data('before') != $(this).html()){
/* do your stuff here - like ajax save */
alert('I promise, I have changed!');
}
});
Para evitar temporizadores e botões "salvar", você pode usar um evento de desfoque que é acionado quando o elemento perde o foco. mas, para ter certeza de que o elemento foi realmente alterado (não apenas focado e desfocado), seu conteúdo deve ser comparado à sua última versão. ou use o evento keydown para definir algum sinalizador "sujo" nesse elemento.
Uma resposta simples no JQuery, eu apenas criei este código e achei que seria útil para outros também
var cont;
$("div [contenteditable=true]").focus(function() {
cont=$(this).html();
});
$("div [contenteditable=true]").blur(function() {
if ($(this).html()!=cont) {
//Here you can write the code to run when the content change
}
});
$("div [contenteditable=true]")irá selecionar todos os filhos de uma div, direta ou indiretamente, que sejam editáveis por conteúdo.
Resposta não JQuery ...
function makeEditable(elem){
elem.setAttribute('contenteditable', 'true');
elem.addEventListener('blur', function (evt) {
elem.removeAttribute('contenteditable');
elem.removeEventListener('blur', evt.target);
});
elem.focus();
}
Para usá-lo, chame (digamos) um elemento de cabeçalho com id = "myHeader"
makeEditable(document.getElementById('myHeader'))
Esse elemento agora será editável pelo usuário até perder o foco.
O evento onchange não é acionado quando um elemento com o atributo contentEditable é alterado. Uma abordagem sugerida pode ser adicionar um botão, "salvar" a edição.
Verifique este plugin que lida com o problema dessa maneira:
Usar DOMCharacterDataModified em MutationEvents levará ao mesmo. O tempo limite está configurado para evitar o envio de valores incorretos (por exemplo, no Chrome, tive alguns problemas com a tecla de espaço)
var timeoutID;
$('[contenteditable]').bind('DOMCharacterDataModified', function() {
clearTimeout(timeoutID);
$that = $(this);
timeoutID = setTimeout(function() {
$that.trigger('change')
}, 50)
});
$('[contentEditable]').bind('change', function() {
console.log($(this).text());
})
DOMCharacterDataModifiednão será acionado quando o usuário modificar o texto existente, por exemplo, aplicando negrito ou itálico. DOMSubtreeModifiedé mais apropriado neste caso. Além disso, as pessoas devem lembrar que os navegadores herdados não suportam esses eventos.
Eu construí um plugin jQuery para fazer isso.
(function ($) {
$.fn.wysiwygEvt = function () {
return this.each(function () {
var $this = $(this);
var htmlold = $this.html();
$this.bind('blur keyup paste copy cut mouseup', function () {
var htmlnew = $this.html();
if (htmlold !== htmlnew) {
$this.trigger('change')
}
})
})
}
})(jQuery);
Você pode simplesmente ligar $('.wysiwyg').wysiwygEvt();
Você também pode remover / adicionar eventos, se desejar
innerHTMLé caro). Eu recomendaria usar o inputevento onde ele existe e voltar a algo assim, mas com algum tipo de devolução.
Em Angular 2+
<div contentEditable (input)="type($event)">
Value
</div>
@Component({
...
})
export class ContentEditableComponent {
...
type(event) {
console.log(event.data) // <-- The pressed key
console.log(event.path[0].innerHTML) // <-- The content of the div
}
}
Por favor, siga a seguinte solução simples
Em .ts:
getContent(innerText){
this.content = innerText;
}
em .html:
<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>
Confira essa ideia. http://pastie.org/1096892
Eu acho que está perto. O HTML 5 realmente precisa adicionar o evento de alteração às especificações. O único problema é que a função de retorno de chamada avalia se (antes == $ (this) .html ()) antes que o conteúdo seja realmente atualizado em $ (this) .html (). setTimeout não funciona, e é triste. Diz-me o que pensas.
Com base na resposta de @ balupton:
$(document).on('focus', '[contenteditable]', e => {
const self = $(e.target)
self.data('before', self.html())
})
$(document).on('blur', '[contenteditable]', e => {
const self = $(e.target)
if (self.data('before') !== self.html()) {
self.trigger('change')
}
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
document.getElementById("editor").oninput = function() { ...}.