【问题标题】:contenteditable change eventscontenteditable 更改事件
【发布时间】:2009-09-07 23:15:12
【问题描述】:

当用户使用contenteditable 属性编辑div 的内容时,我想运行一个函数。什么相当于 onchange 事件?

我正在使用 jQuery,因此任何使用 jQuery 的解决方案都是首选。谢谢!

【问题讨论】:

标签: javascript jquery html contenteditable


【解决方案1】:

2022 年更新

正如 cmets 中所指出的,这并不能回答所提出的问题,即需要等效于 change 事件而不是 input 事件。但是,我将保留原样。

原答案

我建议将侦听器附加到可编辑元素触发的关键事件,但您需要注意 keydownkeypress 事件在内容本身更改之前触发。这不会涵盖更改内容的所有可能方式:用户还可以使用编辑或上下文浏览器菜单中的剪切、复制和粘贴,因此您可能还想处理 cut copypaste 事件.此外,用户可以删除文本或其他内容,因此那里有更多事件(例如mouseup)。您可能希望轮询元素的内容作为后备。

2014 年 10 月 29 日更新

HTML5 input event 是长期的答案。在撰写本文时,当前 Mozilla(来自 Firefox 14)和 WebKit/Blink 浏览器支持 contenteditable 元素,但不支持 IE。

演示:

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

演示:http://jsfiddle.net/ch6yn/2691/

【讨论】:

  • 我还应该补充一点,可以通过使用浏览器的编辑菜单或上下文菜单来剪切、复制或粘贴内容来更改可编辑元素,因此您需要处理 cut、@987654337 @ 和 paste 支持它们的浏览器中的事件(IE 5+、Firefox 3.0+、Safari 3+、Chrome)
  • 你可以使用keyup,不会有时间问题。
  • @Blowsie:是的,但是在事件触发之前,您可能会因自动重复按键而发生大量更改。还有其他方法可以更改此答案未考虑的可编辑文本,例如通过“编辑”和上下文菜单进行拖放和编辑。从长远来看,这一切都应该被 HTML5 input 事件所覆盖。
  • 我正在使用 debounce 进行 keyup。
  • @AndroidDev I testet Chrome,输入事件也会根据规范要求在复制粘贴和剪切时触发:w3c.github.io/uievents/#events-inputevents
【解决方案2】:

这是一个更高效的版本,它使用on 处理所有内容可编辑项。它基于此处的热门答案。

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

项目在这里:https://github.com/balupton/html5edit

【讨论】:

  • 非常适合我,感谢 CoffeeScript 和 Javascript
  • 在 contenteditable 模糊之前,此代码不会捕捉到一些更改。例如:拖放、从上下文菜单剪切和从浏览器窗口粘贴。我已经设置了一个使用此代码的示例页面,您可以与 here 进行交互。
  • @jrullmann 是的,我在拖放图像时遇到了此代码的问题,因为它不听图像加载(在我的情况下处理调整大小操作是个问题)
  • @jrullmann 非常好,即使在使用 javascript 更改值时也可以使用,请在控制台中尝试:document.getElementById('editor_input').innerHTML = 'ssss'。缺点是需要IE11
  • @NadavB 它不应该,因为这就是 "if ($this.data('before') !== $this.html()) {" 的用途
【解决方案3】:

考虑使用MutationObserver。这些观察者旨在对 DOM 中的变化做出反应,并作为Mutation Events 的高性能替代品。

优点:

  • 发生任何更改时触发,这很难通过听其他答案所建议的关键事件来实现。例如,所有这些都很好用:拖放、斜体、通过上下文菜单复制/剪切/粘贴。
  • 在设计时考虑到了性能。
  • 简单、直接的代码。与监听 10 个事件的代码相比,监听一个事件的代码更容易理解和调试。
  • Google 有一个出色的 mutation summary library,这使得使用 MutationObservers 变得非常容易。

缺点:

  • 需要最新版本的 Firefox (14.0+)、Chrome (18+) 或 IE (11+)。
  • 了解新的 API
  • 关于最佳实践或案例研究的信息还不是很多

了解更多:

  • 我写了一点snippet 来比较使用 MutationObserers 处理各种事件。我使用了 balupton 的代码,因为他的 answer 获得了最多的支持。
  • Mozilla 在 API 上有一个很棒的页面
  • 看看MutationSummary

【讨论】:

  • 它与 HTML5 的 input 事件(所有支持突变观察器的 WebKit 和 Mozilla 浏览器中的 contenteditable 都支持)不太一样,因为 DOM 突变也可以通过脚本发生作为用户输入,但对于这些浏览器来说,这是一个可行的解决方案。我想它对性能的影响也比input 事件更严重,但我没有确凿的证据。
  • +1,但您是否意识到 Mutation Events 不会在 contenteditable 中报告换行的影响?在你的 sn-p 中按 enter。
  • @citykid:那是因为 sn-p 只监视字符数据的变化。也可以观察 DOM 结构的变化。例如,请参阅jsfiddle.net/4n2Gz/1
  • Internet Explorer 11+ 支持Mutation Observers
  • 我能够验证(至少在 Chrome 43.0.2357.130 中)HTML5 input 事件在每种情况下都会触发(特别是拖放、斜体、复制/剪切/粘贴)菜单)。我还用 cmd/ctrl+b 测试了粗体并得到了预期的结果。我还检查并能够验证 input 事件 不会 触发程序更改(这似乎很明显,但可以说与相关 MDN 页面上的一些令人困惑的语言相反;见底部这里的页面看看我的意思:developer.mozilla.org/en-US/docs/Web/Events/input)
【解决方案4】:

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

【讨论】:

  • 这个删除事件是什么?
【解决方案5】:

我已经像这样修改了 lawwantsin 的答案,这对我有用。我使用 keyup 事件而不是 keypress,效果很好。

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

【讨论】:

  • 正是我需要的。删除了 keyup,因为我需要在元素失去焦点时触发警报
【解决方案6】:

两种选择:

1) 对于现代(常青)浏览器: “输入”事件将充当替代“更改”事件。

https://developer.mozilla.org/en-US/docs/Web/Events/input

document.querySelector('div').addEventListener('input', (e) => {
    // Do something with the "change"-like event
});

<div oninput="someFunc(event)"></div>

或(使用 jQuery)

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

2) 考虑到 IE11 和现代(常绿)浏览器: 这会监视 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 });

【讨论】:

  • 只需使用input 事件!如果您需要支持 IE11,请使用突变观察器。爱它。简明扼要。
  • 并非如此。关键是 OP 想要对最终更改做一些事情(change 事件会做什么),而input 在您键入时触发。我正在发出 AJAX 请求——我不想在每次按键时都这样做。许多其他答案解释了如何处理。
【解决方案7】:

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

【讨论】:

  • 这看起来很有趣。有人可以提供一些解释吗? ?
【解决方案8】:

这对我有用:

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

【讨论】:

    【解决方案9】:

    当我调查这个主题时,这个帖子非常有帮助。

    我已将此处可用的一些代码修改为 jQuery 插件,因此它采用可重复使用的形式,主要是为了满足我的需要,但其他人可能会喜欢使用 contenteditable 标签快速启动的更简单的界面。

    https://gist.github.com/3410122

    更新:

    由于其越来越受欢迎,该插件已被Makesites.org采用

    发展将从这里继续:

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

    【讨论】:

      【解决方案10】:

      为避免使用计时器和“保存”按钮,您可以使用当元素失去焦点时触发的 blur 事件。但要确保元素实际上已更改(不仅仅是聚焦和散焦),应将其内容与其上一个版本进行比较。或使用 keydown 事件在此元素上设置一些“脏”标志。

      【讨论】:

        【解决方案11】:

        这是我最终使用的解决方案,效果非常好。我改用 $(this).text() 是因为我只使用了一个可编辑内容的单行 div。但是你也可以这样使用 .html() ,你不必担心全局/非全局变量的范围,并且之前实际上是附加到编辑器 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!');
            }
        });
        

        【讨论】:

          【解决方案12】:

          非 JQuery 答案...

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

          要使用它,调用(比如说)一个带有 id="myHeader" 的标题元素

          makeEditable(document.getElementById('myHeader'))
          

          用户现在可以编辑该元素,直到它失去焦点。

          【讨论】:

          • 呃,为什么没有解释就投反对票?这对写答案的人和后来阅读答案的每个人都是有害的。
          • 这段代码创建了一个元素contenteditable,直到它失去焦点。我不认为这回答了这个问题。问题是“当contenteditable 元素发生变化时如何运行函数?”
          【解决方案13】:

          在 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 
           }
          }
          
          
          

          【讨论】:

            【解决方案14】:

            JQuery 中的一个简单答案,我刚刚创建了这段代码,并认为它对其他人也有帮助

                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]") 将直接或间接选择 div 的所有子元素,这些子元素是 contenteditable。
            【解决方案15】:

            您需要使用输入事件类型

            演示

            HTML

            <div id="editor" contenteditable="true" >Some text here</div>
            

            JS

            const input = document.getElementById('editor');
            
            
            input.addEventListener('input', updateValue);
            
            function updateValue(e) {
              console.log(e.target);
            }
            

            know more

            【讨论】:

              【解决方案16】:

              当具有 contentEditable 属性的元素发生更改时,不会触发 onchange 事件,建议的方法是添加一个按钮,以“保存”版本。

              检查这个以这种方式处理问题的插件:

              【讨论】:

              • 为了后代,十年前,不需要“保存”按钮,检查接受的答案的 jsfiddle
              【解决方案17】:

              使用DOMCharacterDataModified under MutationEvents 将导致相同的结果。设置超时是为了防止发送不正确的值(例如,在 Chrome 中,我遇到了一些空格键问题)

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

              JSFIDDLE example

              【讨论】:

              • +1 - DOMCharacterDataModified 不会在用户修改现有文本时触发,例如应用粗体或斜体。 DOMSubtreeModified 在这种情况下更合适。此外,人们应该记住,旧版浏览器不支持这些事件。
              • 请注意,由于性能问题,W3c 对 Mutation Events 进行了贬值。更多可以在this Stack Overflow question找到。
              【解决方案18】:

              我为此构建了一个 jQuery 插件。

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

              您可以直接拨打$('.wysiwyg').wysiwygEvt();

              如果您愿意,您还可以删除/添加事件

              【讨论】:

              • 随着可编辑内容的变长(innerHTML 很昂贵),这将变得缓慢和滞后。我建议使用存在的input 事件并回退到类似的东西,但有某种去抖动。
              【解决方案19】:

              对我来说,我想检查输入是否有效。

              如果有效,则更新,否则显示错误消息并保持与之前相同的值。

              技巧:编辑完成后,通常会触发blur事件。

              示例

              <span contenteditable="true">try input somethings.</span>
              <script>
                const elem = document.querySelector(`span`)
                let oldValue = elem.innerText
                elem.onkeydown = (keyboardEvent) => {
                  if (keyboardEvent.key === "Enter") {
                    elem.blur() // set focusout
                  }
                }
                elem.onblur = (e) => {
                  const curValue = elem.innerText
                  if (curValue === oldValue) {
                    return
                  }
                  if (curValue.length <= 50) { // ? Input your conditions.
                    // ? fail
                    elem.innerText = oldValue
                    
                    // (Optional) Add error message
                    elem.insertAdjacentHTML("beforeend", `<span style="margin-left:5px;color:red">error length=${curValue.length}. Must greater than 50. undo to the previous value.</span>`)
                    const errMsg = elem.querySelector(`span`)
                    setTimeout(() => errMsg.remove(), 3500) // wait 3.5 second, and then remove it.
                    return
                  }
                  // ? OK, update
                  oldValue = curValue
                }
              </script>

              【讨论】:

                【解决方案20】:

                看看这个想法。 http://pastie.org/1096892

                我认为它很接近。 HTML 5 确实需要将更改事件添加到规范中。唯一的问题是回调函数在 $(this).html() 中的内容实际更新之前评估 if (before == $(this).html())。 setTimeout 不起作用,这很可悲。让我知道你的想法。

                【讨论】:

                • 试试 keyup 而不是 keypress。
                【解决方案21】:

                基于@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')
                	}
                })
                &lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;

                【讨论】:

                • 请记住,如果您对答案投反对票,您有责任发表评论。解释为什么答案不令人满意(除非有人已经发表了评论,而您只是同意他们)。
                • jQuery 永远不是一个合适的答案@JackSteam
                猜你喜欢
                • 2016-11-21
                • 1970-01-01
                • 2023-04-02
                • 2011-09-09
                • 2019-01-12
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多