Respostas:
Eu sugiro ouvintes associados aos principais eventos disparados pelo elemento editável, mas você precisa estar ciente de que keydown
e keypress
eventos 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
copy
e paste
. Além disso, o usuário pode soltar texto ou outro conteúdo, para que haja mais eventos ( mouseup
por 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 contenteditable
elementos 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
, copy
e paste
eventos em navegadores que os apoiam (IE 5+, o Firefox 3.0+, Safari 3+, Chrome)
input
evento HTML5 .
Aqui está uma versão mais eficiente que é usada on
para 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:
input
evento HTML5 (que é suportado contenteditable
em 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 input
evento também, mas não tenho provas concretas disso.
input
evento 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 input
evento 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());
})
DOMCharacterDataModified
nã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 input
evento 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() { ...}
.