Javascript:意外的焦点模糊

3
在我学习Javascript的过程中,我试图实现一个所见即所得的代码编辑器。但我并没有像普通人那样用content-editable的div或textarea, 我参考了Ajax.org的ACE的做法。
我创建了一个listener div,并设置tabIndex为0来处理所有的输入事件。所有编辑器的组件,如文本区域、光标和侧边栏都是该listener的子节点。由于我实现了鼠标事件处理程序,我一直存在一个问题:当mousedown事件在listener上发生时,焦点会丢失。显然,即使mousedown事件在listener中发生,blur事件也会被触发。
经过注释某些方法调用并尝试确定问题的真正原因后,我发现罪魁祸首是render方法。现在,我在每次输入之后调用此方法,因此在调用它进行mousedow n事件时,焦点丢失非常奇怪。如果注释掉render方法,则在mousedown事件时不会触发blur事件。
进一步研究发现,注释掉替换text container的innerHTML部分后,问题就消失了。这又是非常奇怪的,因为我在render函数中的多个地方都使用了replaceInnerHTML函数,但似乎只有特别更新文本div时才会出现问题。
以下是我发现的replaceInnerHTML方法(可在http://blog.stevenlevithan.com/archives/faster-than-innerhtml查看)。
function replaceInnerHTML(el, html) {
    var newEl = el.cloneNode(false);

    newEl.innerHTML = html;

    el.parentNode.replaceChild(newEl, el);

    return newEl;
}

这是文本图层的更新函数,省略它将不会出现此问题:

TextLayer.prototype.update = function(config) {
    html = [];

    var top = config.topRow;
    var last = config.lastRow;

    for(var i=top; i<=last; i++) {
        var line = this._session.getLine(i);
        if(line) {
            html.push('<div class="line" style="height:'+this.$characterSize.height+'px;">');
            this.renderLine(html, line);
            html.push('</div>');
        }       
    }   

    this._text = replaceInnerHTML(this._text, html.join(''));
    this.setSize(config.totalWidth, config.scrollHeight);
};

为什么仅仅替换特定节点的innerHTML会导致焦点模糊,而且只有在mousedown事件的情况下才会出现这种情况?或者说我错了方向,这个问题是由其他原因引起的吗?
编辑:感谢pimvdb,这里有一个重现问题的fiddle。blur事件似乎是由于在mousedown事件上重新构造节点而触发的。我不知道该如何解决这个问题。我已经尝试使用文档片段代替innerHTML,并尝试使用setTimeout调度更新,但两者都没有成功。我应该怎么做才能解决这个问题?

1
我正在尝试重现 - 这个JSFiddle是否重现了这个问题?http://jsfiddle.net/YVAmh/ (单击span以进行替换会丢失焦点,但单击div则不会。) - pimvdb
是的,它准确地再现了这个问题。我创建了另一个 fiddle 来展示我实际想要做的事情。http://jsfiddle.net/wG4Yy/39/ (在按下 tab 键时获得焦点,键盘事件可以正常工作。但一旦有 mousedown,就会触发 blur,键盘事件将不再起作用。) - Rikonator
我一直在摆弄,但我还没有找出为什么它会失去焦点。一个(临时?)的解决方案是在浏览器处理mousedown/focus事件后手动聚焦:http://jsfiddle.net/wG4Yy/41/. - pimvdb
谢谢!我已经尝试了几种不同的方法,并且现在使用 setTimeout 来调用 TextLayer.update。像这样:http://jsfiddle.net/wG4Yy/42/. 它可以起作用,但正如你所说,感觉是一种弱解决方案。尽管如此,它仍然可以工作,我真的很感激。你能把它发布为答案而不是评论吗?这样我就可以接受它了。再次感谢! - Rikonator
1个回答

1

当您想要一个元素获得焦点时,似乎您必须单击停留在那里的元素。如果您将其替换为另一个元素,则浏览器显然不会被欺骗以便将焦点放在该元素上。

您可以让浏览器完成其mousedown/focus等过程,然后手动聚焦该元素。这可以通过调用setTimeout(..., 0)来实现:http://jsfiddle.net/wG4Yy/41/

setTimeout(function() {
    wrapper.focus();
}, 0);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接