3

我目前已将我的 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
    }
});

如果有任何不清楚的地方,请询问,我会为您澄清:)

4

4 回答 4

1

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

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

您可以使用 underscore 的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);
于 2013-01-03T21:58:48.570 回答
1

你的原作太过分了。所有你需要的是inputpropertychange事件。

2016 更新

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

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

这是一个演示使用input事件并回退到propertychangeIE <= 8 的答案:

只捕捉改变输入的按键?

于 2013-01-03T23:13:37.533 回答
0

这似乎在 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 测试。

于 2013-01-03T22:15:19.563 回答
0

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

$(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...
        }
    };
}()));
于 2013-01-03T22:18:01.677 回答