【问题标题】:How do I detect any change to a textarea?如何检测文本区域的任何更改?
【发布时间】:2013-01-03 21:51:59
【问题描述】:

我目前已将我的 textarea 绑定到几个似乎有效的事件。但是,问题在于事件会重叠并触发多次,这反过来又会大大降低性能。

我想要做的几乎就是捕捉到文本区域的任何更改:单击、粘贴、keyup、keydown、右键单击上下文菜单编辑(右键单击、剪切/删除/粘贴)、拖放等。这有可以跨浏览器工作,至少到 IE8。当您使用箭头键或类似键在文本区域中移动插入符号时,必须触发事件(我根据插入符号位置处理更改等)。

我不能使用任何重大延迟。一旦你对 textarea 做一些事情,事件必须立即触发并执行我在那里的任何代码。

我目前正在使用 jQuery 来绑定事件,但我可以使用纯 javascript 解决方案,只要它可以跨浏览器工作并执行我想要的操作。

这是我目前使用的代码:

var deadKeycodes = [16, 17, 18, 19, 20, 
                    27, 33, 34, 35, 36,
                    38, 40, 44, //37 = left arrow and 39 = right arrow removed, it needs to trigger on those
                    45, 112, 113, 114, 115,
                    116, 117, 118, 119, 120,
                    121, 122, 123, 144, 145];

$(original).bind('propertychange keyup keydown input click', function(e) { 
    if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) { // prevent execution when pressing a 'dead' key
         //do stuff here
    }
});

如果有什么不清楚的地方就问吧,我会为你澄清的:)

【问题讨论】:

  • keydown 和 click 是你真正需要的,如果 click 没有覆盖,也许粘贴和剪切事件也是如此。
  • Keyup 和 keydown 在不同的时间触发。在文本区域中按住一个键并查看(并查看降价预览何时更新)。一般来说,keyup 和 keydown 不能很好地确定输入何时发生变化(尽管您可以将两者都用于可接受的情况)。
  • keydown 与 setTimeout 设置为 0 配对,非常适合在 keydown 时立即检测更改,而不是等待 keyup。

标签: javascript jquery events jquery-events


【解决方案1】:

我认为从阻止多个事件触发的意义上说,您无法真正“解决”问题。 Keyup 和 keydown 确实是不同的事件,并且发生在不同的时间。如果您想对两者都做出响应(您可能会这样做,因为按下键和向上键都可能会更改文本区域),则需要包含这两个事件。但是,大多数情况下,它们几乎会同时触发(并且连续多次触发),正如您所指出的那样,这可能会造成性能问题。

相反,您可能应该考虑触发 throttleddebounced 回调。受限制的回调只会每 n 毫秒触发一次(适用于可能被调用过多的函数)。去抖回调只会在事件流完成后触发;自上次回调后经过 n 毫秒后。

您可以使用下划线的debouncethrottle 函数轻松完成此操作。

类似:

debouncedFn = _.debounce(function(e) { 
    if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) { // prevent execution when pressing a 'dead' key
         //do stuff here
    }
}, 100);

$(original).bind('propertychange keyup keydown input click', debouncedFn);

【讨论】:

  • 嗯,我会用这个作为最后的手段。如果我真的不需要,我不想添加不必要的库。
  • 如果是这样,请自己破解而不是使用库。它最多需要 20 行。只需使用 Date 对象并减去。
  • 但是说真的,最好还是使用这个库。 Underscore 是超级紧凑的(特别是与 jQuery 相比——它小了 20 倍),你应该将它(或实现类似目的的东西,如 sugar)用于任何超过几百行的 javascript 项目。厌恶核心库通常不是一个好主意。
【解决方案2】:

你的原作太过分了。您只需要inputpropertychange 事件。

2016 年更新

原来链接的页面现在已经消失了。这是它的一个稍微次优的快照:

http://web.archive.org/web/20140810185102/http://whattheheadsaid.com/2010/09/effectively-detecting-user-input-in-javascript

这是一个答案,演示了使用 input 事件并在 IE propertychange:

Catch only keypresses that change input?

【讨论】:

  • 我需要捕获 keydown/up 的原因是因为当用户使用箭头在 textarea 中移动时我需要获取插入符号的位置。
  • @Lindrian:啊,所以“任何更改”包括对选择的更改。我不确定这个问题是否清楚。
  • 如果不清楚,我很抱歉。我提到了右键单击上下文菜单编辑,但这可能不够清楚。我现在已经澄清了。
  • @Lindrian:无需道歉。无论如何,我的答案并没有花很长时间来写:)
  • 链接似乎已失效。
【解决方案3】:

为防止事件重叠,您可以存储上次调用的时间。在执行事件的回调之前,请测试是否有足够的时间再次触发。

$(original).bind('propertychange keyup keydown input click', (function () {
    var lastCallTime = 0;

    return function (e) {
        if (Date.now() - lastCallTime < 50) { return; } // Too little time has passed--exit

        if (!Array.prototype.indexOf || deadKeycodes.indexOf(e.keyCode) == -1) {
            lastCallTime = Date.now();

            // your code...
        }
    };
}()));

【讨论】:

    【解决方案4】:

    这似乎在 IE7-9 和 Chrome 中解决了,其余的还没有测试。无论更改是什么,每次更改只会发生一个 console.log。如果没有更改,则不会记录任何内容:http://jsfiddle.net/SJN6J/2/

    var timer;
    $("textarea").on("keydown paste cut", function(){
        clearTimeout(timer);
        var origvalue = this.value, self = this;
        timer = setTimeout(function(){
            if ( origvalue !== self.value ) {
                console.log("it changed!");
                // do stuff because content changed
            }
        },0);
    });
    

    注意:我的 IE7-8 测试是在 IE9 更改浏览器模式的情况下进行的,因此您可能想做真正的 IE7-8 测试。

    【讨论】:

    • 这种 kind of 有效,但我需要在输入时触发,因为如果您选择并右键单击 -> 删除,它不会触发。但是,当我添加“输入”时,它会中断。
    猜你喜欢
    • 2016-02-05
    • 1970-01-01
    • 2014-12-23
    • 1970-01-01
    • 1970-01-01
    • 2021-01-02
    • 2021-08-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多