Pregunta

Quiero ejecutar una función cuando un usuario edite el contenido de un div con contenteditable atributo. ¿Cuál es el equivalente de un onchange ¿evento?

Estoy usando jQuery, por lo que se prefiere cualquier solución que use jQuery. ¡Gracias!

¿Fue útil?

Solución

Sugeriría adjuntar a los oyentes a eventos clave disparados por el elemento editable, aunque debe tener en cuenta que keydown y keypress Los eventos se disparan antes de que se cambie el contenido en sí. 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 de edición o contexto del navegador, por lo que es posible que desee manejar el cut copy y paste eventos también. Además, el usuario puede eliminar el 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 un respaldo.

Actualización 29 de octubre de 2014

los Html5 input evento es la respuesta a largo plazo. Al momento de escribir, es compatible con contenteditable Elementos en Mozilla actual (de Firefox 14) y navegadores WebKit/Blink, pero no es decir.

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>

Manifestación: http://jsfiddle.net/ch6yn/2691/

Otros consejos

Aquí hay una versión más eficiente que usa on Para todos los contento. 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

Considere usar Mutationobserver. Estos observadores están diseñados para reaccionar a los cambios en el DOM y como reemplazo de rendimiento a Eventos de mutación.

Pros:

  • Dispara cuando ningún Se produce un cambio, que es difícil de lograr escuchando eventos clave como lo sugieren otras respuestas. Por ejemplo, todos estos funcionan bien: arrastrar y soltar, en cursiva, copiar/cortar/pegar a través del menú contextual.
  • Diseñado con el rendimiento en mente.
  • Código simple y directo. Es mucho más fácil de entender y depurar un código que escucha un evento en lugar de codificar que escucha 10 eventos.
  • Google tiene un excelente biblioteca de resumen de mutación lo que hace que el uso de MutationObServers sea muy fácil.

Contras:

  • Requiere una versión muy reciente de Firefox (14.0+), Chrome (18+) o IE (11+).
  • Nueva API para entender
  • Aún no hay mucha información disponible sobre las mejores prácticas o estudios de casos.

Aprende más:

  • Escribí un poco retazo comparar el uso de MutationObserers con el manejo de una variedad de eventos. Usé el código de Balupton desde su responder tiene la mayor cantidad de votos
  • Mozilla tiene una excelente página en la API
  • Echa un vistazo al Mutacionesmarias biblioteca

no jQuery rápido y sucio responder:

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);
});

He modificado la respuesta de Lawwantsin así y esto funciona para mí. Utilizo el evento KeyUp en lugar de KeyPress 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')});

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 />

Dos opciones:

1) Para los navegadores modernos (de hoja perenne):El evento de "entrada" 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) tener en cuenta los navegadores IE11 y modernos (de hoja perenne):Esto observa los cambios de 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 });

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.

He modificado parte del código disponible aquí en un complemento jQuery, por lo que está en una forma reutilizable, principalmente para satisfacer mis necesidades, pero otros pueden apreciar una interfaz más simple para saltar con etiquetas contentales.

https://gist.github.com/3410122

Actualizar:

Debido a su creciente popularidad, el complemento ha sido adoptado por Mateites.org

El desarrollo continuará desde aquí:

https://github.com/makesites/jquery-contentitable

Aquí está la solución que terminé usando y funciona fabulosamente. Utilizo $ (esto) .Text () en su lugar porque solo estoy usando una línea DIV que es editable. Pero también puede usar .html () de esta manera no tiene que preocuparse por el alcance de una variable global/no global y el anterior realmente está adjunto 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!');
    }
});

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, llame (digamos) un elemento de encabezado con id = "myheader"

makeEditable(document.getElementById('myHeader'))

Ese elemento ahora será editable por el usuario hasta que pierda el enfoque.

El evento OnChange no se dispara cuando se cambia un elemento con el atributo contento, un enfoque sugerido podría ser agregar un botón, a "ahorrar" la edición.

Verifique este complemento que maneja el problema de esa manera:

Para evitar los temporizadores y los botones de "guardar", puede usar el evento de desenfoque que los disparos cuando el elemento pierde el enfoque. Pero para asegurarse de que el elemento realmente cambió (no solo enfocado y desfigurado), su contenido debe compararse con su última versión. O use el evento Keydown para establecer una bandera "sucia" en este elemento.

Usando Domcharacterdatamodified bajo mutaciones eventos 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 clave 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());
})

Ejemplo de jsfiddle

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

Una respuesta simple en jQuery, acabo de crear este código y pensé que también será útil para los demás.

    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
        }           
    });

Revise 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 si (antes == $ (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>

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top