web-dev-qa-db-fra.com

événements de changement contentables

Je souhaite exécuter une fonction lorsqu'un utilisateur modifie le contenu d'un attribut div avec contenteditable. Quel est l'équivalent d'un événement onchange?

J'utilise jQuery, donc toutes les solutions utilisant jQuery sont préférées. Merci!

307
Jourkey

Je suggérerais d'attacher des écouteurs aux événements clés déclenchés par l'élément éditable, bien que vous sachiez bien que les événements keydown et keypress sont déclenchés avant que le contenu lui-même ne soit modifié. Cela ne couvrira pas tous les moyens possibles de changer le contenu: l'utilisateur peut également utiliser couper, copier et coller à partir des menus de modification ou du navigateur contextuel, de sorte que vous souhaiterez peut-être également gérer les événements cutcopy et paste. En outre, l'utilisateur peut supprimer du texte ou un autre contenu, de sorte qu'il y ait plus d'événements à cet endroit (mouseup, par exemple). Vous souhaiterez peut-être interroger le contenu de l'élément comme solution de secours.

UPDATE 29 octobre 2014

L'événement HTML5 input est la réponse à long terme. Au moment de la rédaction de ce document, il est pris en charge pour les éléments contenteditable des navigateurs Mozilla (à partir de Firefox 14) et WebKit/Blink actuels, mais pas pour IE.

Démo:

document.getElementById("editor").addEventListener("input", function() {
    console.log("input event fired");
}, false);
<div contenteditable="true" id="editor">Please type something in here</div>

Démo: http://jsfiddle.net/ch6yn/2691/

249
Tim Down

Voici une version plus efficace qui utilise on pour tous les contenteditables. C'est basé sur les meilleures réponses ici.

$('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');
    }
});

Le projet est ici: https://github.com/balupton/html5edit

173
balupton

Pensez à utiliser MutationObserver . Ces observateurs sont conçus pour réagir aux modifications apportées au DOM et constituer un remplacement performant de événements de mutation .

Avantages:

  • Se déclenche lorsque tout changement se produit, ce qui est difficile à obtenir en écoutant les événements clés suggérés par d'autres réponses. Par exemple, tout cela fonctionne bien: glisser-déposer, italiciser, copier/couper/coller dans le menu contextuel.
  • Conçu avec la performance à l'esprit.
  • Code simple et direct. Il est beaucoup plus facile de comprendre et de déboguer un code écoutant un événement plutôt qu'un code écoutant 10 événements.
  • Google possède une excellente bibliothèque de synthèse des mutations qui facilite l'utilisation de MutationObservers.

Les inconvénients:

  • Nécessite une version très récente de Firefox (14.0+), Chrome (18+) ou IE (11+).
  • Nouvelle API à comprendre
  • Pas beaucoup d'informations disponibles sur les meilleures pratiques ou les études de cas

Apprendre encore plus:

  • J'ai écrit un petit extrait pour comparer l'utilisation de MutationObserers à la gestion de divers événements. J'ai utilisé le code de balupton puisque son réponse a le plus de votes positifs.
  • Mozilla a une excellente page sur l'API
  • Jetez un coup d'œil à la bibliothèque MutationSummary
49
jrullmann

non jQuery rapide et sale réponse:

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

J'ai modifié la réponse de lawwantsin comme suit et cela fonctionne pour moi. J'utilise l'événement keyup au lieu de la frappe qui fonctionne très 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')});
19
Dennkster

Ce fil de discussion m'a été très utile lors de mes recherches sur le sujet. 

J'ai modifié une partie du code disponible ici dans un plugin jQuery afin qu'il soit réutilisé, principalement pour répondre à mes besoins, mais d'autres peuvent apprécier une interface plus simple pour démarrer avec des balises contenteditable. 

https://Gist.github.com/3410122

Mettre à jour:

En raison de sa popularité croissante, le plugin a été adopté par Makesites.org

Le développement continuera d'ici:

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

3
tracend

Voici ce qui a fonctionné pour moi:

   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);
            }
   });
3
gregory whiteside

Deux options:

1) Pour les navigateurs modernes (à feuilles persistantes): L'événement "input" agirait comme un événement "change" alternatif.

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 (avec jQuery)

$('div').on('click', function(e) {
    // Do something with the "change"-like event
});

2) Pour tenir compte des navigateurs IE11 et modernes (à feuilles persistantes): Ceci surveille les changements d'éléments et leur contenu à l'intérieur du 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 });
2
bit-less

Voici la solution que j'ai finalement utilisée et qui fonctionne à merveille. J'utilise plutôt $ (this) .text () parce que j'utilise simplement un div d'une ligne dont le contenu est éditable. Mais vous pouvez également utiliser .html () de cette façon, vous n'avez pas à vous soucier de la portée d'une variable globale/non globale et l'avant est en fait attaché à l'éditeur 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!');
    }
});
2
user740433

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

1
superwf

Pour éviter les temporisations et les boutons "Enregistrer", vous pouvez utiliser un événement de flou qui se déclenche lorsque l'élément perd le focus. mais pour être sûr que l'élément a réellement été modifié (pas seulement focalisé et défocalisé), son contenu doit être comparé à sa dernière version. ou utilisez l'événement keydown pour définir un indicateur "sale" sur cet élément.

0
joox

L'utilisation de DOMCharacterDataModified sous MutationEvents conduira à la même chose. La temporisation est configurée pour empêcher l'envoi de valeurs incorrectes (par exemple, dans Chrome, je rencontrais des problèmes avec la touche espace).

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

Exemple JSFIDDLE

0
janfabian

Une réponse simple dans JQuery, je viens de créer ce code et j'ai pensé qu'il serait utile pour les autres aussi

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

Réponse non JQuery ...

function makeEditable(elem){
    elem.setAttribute('contenteditable', 'true');
    elem.addEventListener('blur', function (evt) {
        elem.removeAttribute('contenteditable');
        elem.removeEventListener('blur', evt.target);
    });
    elem.focus();
}

Pour l'utiliser, appelez (par exemple) un élément d'en-tête avec id = "myHeader"

makeEditable(document.getElementById('myHeader'))

Cet élément sera désormais modifiable par l'utilisateur jusqu'à ce qu'il perde le focus.

0
DrMcCleod

J'ai construit un plugin jQuery pour le faire.

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

Vous pouvez simplement appeler $('.wysiwyg').wysiwygEvt();

Vous pouvez également supprimer/ajouter des événements si vous le souhaitez

0
Blowsie

L'événement onchange ne se déclenche pas lorsqu'un élément doté de l'attribut contentEditable est modifié. Une approche suggérée pourrait être d'ajouter un bouton à l'édition "save".

Vérifiez ce plugin qui gère le problème de cette façon:

0
CMS