Respuestas:
Yo sugeriría oyentes correspondientes a los eventos clave disparados por el elemento editable, aunque es necesario tener en cuenta que keydown
y keypress
eventos es despedido antes de que el contenido en sí se cambia. Esto no cubrirá todos los medios posibles para cambiar el contenido: el usuario también puede usar cortar, copiar y pegar desde los menús Editar o contextual del navegador, por lo que es posible que también desee manejar los eventos cut
copy
y paste
. Además, el usuario puede soltar texto u otro contenido, por lo que hay más eventos allí ( mouseup
por ejemplo). Es posible que desee sondear el contenido del elemento como reserva.
ACTUALIZACIÓN 29 de octubre de 2014
El evento HTML5input
es la respuesta a largo plazo. Al momento de escribir, es compatible con contenteditable
elementos en los navegadores actuales de Mozilla (de Firefox 14) y WebKit / Blink, pero no IE.
Manifestación:
document.getElementById("editor").addEventListener("input", function() {
console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>
Demostración: http://jsfiddle.net/ch6yn/2691/
cut
, copy
y paste
eventos en los navegadores que las soportan (IE 5+, Firefox 3.0+, Safari 3+, Chrome)
input
evento HTML5 .
Aquí hay una versión más eficiente que utiliza on
para todos los contentables. Se basa en las mejores respuestas aquí.
$('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');
}
});
El proyecto está aquí: https://github.com/balupton/html5edit
document.getElementById('editor_input').innerHTML = 'ssss'
. El inconveniente es que requiere IE11
Considere usar MutationObserver . Estos observadores están diseñados para reaccionar a los cambios en el DOM y como un reemplazo eficaz para eficaz de los eventos de mutación .
Pros:
Contras:
Aprende más:
input
evento HTML5 (que es compatible con contenteditable
todos los navegadores WebKit y Mozilla que admiten observadores de mutación), ya que la mutación DOM podría ocurrir a través del script y la entrada del usuario, pero es una solución viable para esos navegadores. Me imagino que también podría dañar el rendimiento más que el input
evento, pero no tengo pruebas contundentes de esto.
input
evento HTML5 se dispara en cada una de estas situaciones (específicamente arrastrar y soltar, cursiva, copiar / cortar / pegar a través del menú contextual). También probé negrita w / cmd / ctrl + b y obtuve los resultados esperados. También verifiqué y pude verificar que el input
evento no se dispara en los cambios programáticos (lo que parece obvio, pero podría decirse que es contrario a algún lenguaje confuso en la página MDN relevante; vea la parte inferior de la página aquí para ver lo que quiero decir: desarrollador .mozilla.org / es-es / docs / Web / Events / input )
He modificado la respuesta de lawwantsin así y esto funciona para mí. Uso el evento keyup en lugar de presionar teclas, lo que funciona muy bien.
$('#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')});
no jQuery respuesta rápida y sucia :
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);
});
Dos opciones:
1) Para navegadores modernos (de hoja perenne): el evento "input" actuaría como un evento alternativo de "cambio".
https://developer.mozilla.org/en-US/docs/Web/Events/input
document.querySelector('div').addEventListener('input', (e) => {
// Do something with the "change"-like event
});
o
<div oninput="someFunc(event)"></div>
o (con jQuery)
$('div').on('click', function(e) {
// Do something with the "change"-like event
});
2) Para tener en cuenta los navegadores IE11 y modernos (perennes): Esto busca cambios en los elementos y su contenido dentro del 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 />
Esto es lo que funcionó para mí:
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);
}
});
Este hilo fue muy útil mientras investigaba el tema.
Modifiqué parte del código disponible aquí en un complemento jQuery para que sea reutilizable, principalmente para satisfacer mis necesidades, pero otros pueden apreciar una interfaz más simple para iniciar usando etiquetas contentables.
https://gist.github.com/3410122
Debido a su creciente popularidad, el complemento ha sido adoptado por Makesites.org
El desarrollo continuará desde aquí:
Aquí está la solución que terminé usando y funciona fabulosamente. En su lugar, uso $ (this) .text () porque solo estoy usando un div de una línea que tiene contenido editable. Pero también puede usar .html () de esta manera, no tiene que preocuparse por el alcance de una variable global / no global y lo anterior está realmente unido al 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 y botones "guardar", puede usar un evento de desenfoque que se dispara cuando el elemento pierde el foco. pero para asegurarse de que el elemento haya cambiado realmente (no solo enfocado y desenfocado), su contenido debe compararse con su última versión. o use el evento keydown para establecer una bandera "sucia" en este elemento.
Una respuesta simple en JQuery, acabo de crear este código y pensé que sería útil para otros también
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]")
seleccionará todos los niños de un div, directa o indirectamente, que sean contentables.
Respuesta no JQuery ...
function makeEditable(elem){
elem.setAttribute('contenteditable', 'true');
elem.addEventListener('blur', function (evt) {
elem.removeAttribute('contenteditable');
elem.removeEventListener('blur', evt.target);
});
elem.focus();
}
Para usarlo, invoque (digamos) un elemento de encabezado con id = "myHeader"
makeEditable(document.getElementById('myHeader'))
Ese elemento ahora será editable por el usuario hasta que pierda el foco.
El evento onchange no se activa cuando se cambia un elemento con el atributo contentEditable, un enfoque sugerido podría ser agregar un botón, para "guardar" la edición.
Marque este complemento que maneja el problema de esa manera:
El uso de DOMCharacterDataModified en MutationEvents conducirá a lo mismo. El tiempo de espera está configurado para evitar el envío de valores incorrectos (por ejemplo, en Chrome tuve algunos problemas con la tecla de espacio)
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
no se activará cuando el usuario modifique el texto existente, por ejemplo, aplicando negrita o cursiva. DOMSubtreeModified
Es más apropiado en este caso. Además, las personas deben recordar que los navegadores heredados no admiten estos eventos.
Construí un complemento jQuery para hacer esto.
(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);
Simplemente puedes llamar $('.wysiwyg').wysiwygEvt();
También puede eliminar / agregar eventos si lo desea
innerHTML
es costoso). Recomendaría usar el input
evento donde existe y recurrir a algo como esto pero con algún tipo de rebote.
En 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
}
}
Siga la siguiente solución simple
En .ts:
getContent(innerText){
this.content = innerText;
}
en .html:
<div (blur)="getContent($event.target.innerHTML)" contenteditable [innerHTML]="content"></div>
Mira esta idea. http://pastie.org/1096892
Creo que está cerca. HTML 5 realmente necesita agregar el evento de cambio a la especificación. El único problema es que la función de devolución de llamada evalúa if (before == $ (this) .html ()) antes de que el contenido se actualice realmente en $ (this) .html (). setTimeout no funciona, y es triste. Déjame saber lo que piensas.
Basado en la respuesta 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() { ...}
.