为什么文本区域的选择范围会重置为0?

6

我有一些IMG表情符号,可以作为它们的字符串形式插入到文本框中。下面的脚本必须获取光标位置并将其表示插入到正确的位置,但有时会误触并将选择范围重置为0。

您可以通过在示例中单击笑脸和文本区域内部来查看它的效果。我已经在在文本框内容末尾设置光标后将其重置为0。

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea"></textarea>
</div>

在控制台日志中会看到插入表情符号之前和之后的插入点位置。

1个回答

6

我想你已经知道了,el.value = (before + newText + after);会清除值并用新值替换它,将光标移回字段的开头。

我认为为了使后面的setSelectionRange能够跨浏览器正常工作,该字段必须再次获得焦点。这似乎在Chrome中是正确的,因为这个(加上el.focus())可以工作:

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                el.focus();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea"></textarea>
</div>

在您的评论中,您问道:

如果我不想将焦点设置在文本区域上,是否有解决方案? 我之所以这样问,是因为我试图使UI更加友好,当文本区域获得焦点时隐藏表情符号面板。 因此,当脚本在插入表情符号后设置焦点时,表情符号面板会滑动出去,这并不好。

为了解决这个问题,我尝试在使用 focus 之前抓取活动元素:

// Didn't work
var active = document.activeElement;
el.focus();
el.setSelectionRange(start, end);
if (active) {
    active.focus();
}

当那种方法不起作用时,我使用了el.blur(),虽然我不太喜欢它,但它确实有效:

window.onload = function() {
var doc = window.document;
var smiles = doc.getElementById('chat-smiles-panel').getElementsByTagName('IMG');
        var el    = doc.getElementById('chat-textarea');
        for (var i = 0; i < smiles.length; i++) {
            smiles[i].onclick = function(e) {
                var elem, evt = e?e:event;
                elem = (evt.target)?evt.target:evt.srcElement;
                var newText = elem.getAttribute('data-smile');
                console.log('before', el.selectionStart, el.selectionEnd);
                var start = el.selectionStart;
                var end   = el.selectionEnd;
                var text  = el.value;
                var before = text.substring(0, start);
                var after  = text.substring(end, text.length);
                el.value = (before + newText + after);
                el.focus();  // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                start = end = start + newText.length;
                el.setSelectionRange(start, end);
                el.blur();   // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< Added
                console.log('after', el.selectionStart, el.selectionEnd);
            }
        }
};
<div>
  <div id="chat-smiles-panel">
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/slightly-smiling-face_1f642.png" alt="smiley" data-smile=":smiley:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/rolling-on-the-floor-laughing_1f923.png" alt="lol" data-smile=":lol:" />
    <img width="20" src="https://emojipedia-us.s3.dualstack.us-west-1.amazonaws.com/thumbs/160/apple/155/smiling-face-with-halo_1f607.png" alt="angel" data-smile=":angel:" />
  </div>
  <textarea id="chat-textarea">Testing 1 2 3</textarea>
</div>

我倾向于让表情符号面板变得更加智能,例如增加一个从上次使用开始的一秒延迟,以便您可以添加多个表情符号,但是它会在最后一个表情符号后一秒钟消失。

我相信你是对的,但我无法让这个代码片段表现出任何奇怪的行为。(火狐浏览器) - Pointy
@Pointy - 我之前说错了,但现在我想我是正确的。 :-) 我发送得太快了。你可以在Chrome上看到问题,在Firefox上(对我来说)不会发生。使用OP的片段:如果您在字段中键入一些文本,将光标定位在其中某个位置,然后单击表情符号,它会将其插入到正确的位置,但不会聚焦该字段。如果您再次切换到焦点字段,则光标位于字段的开头。(在Firefox中,它在您单击表情符号时所在的位置。)通过我的更改,单击表情符号会在两个浏览器中都将其插入并使您保持在正确的位置。 - T.J. Crowder
如果我不想将焦点设置在文本区域上,有没有其他解决方案?我问了这个问题是因为我试图使用户界面更加友好,在文本区域获得焦点时隐藏表情符号面板。因此,当脚本插入表情符号后设置焦点时,表情符号面板会滑动消失,这并不好。 - Mik
1
@Mik - 如果你真的找到了一种不需要焦点就能实现它的方法,你会回来发帖并将勾选标记切换到它,然后联系我让我知道吗?那会很酷。 :-) 你也可以向Chromium项目提交问题报告,因为似乎应该可以在没有焦点的情况下设置选择。最后:我刚才注意到,即使使用你最初的代码,Edge在点击表情符号时也不会将焦点从文本区域移开,所以你需要在测试时考虑这一点。(Edge将切换到使用Blink,这应该会使其像Chrome一样运作,但这是未来的事情)。 - T.J. Crowder
1
@T.J.Crowder,不幸的是,我没有找到其他方法,我只是改变了用户界面,并删除了文本区域聚焦时隐藏面板的功能。感谢您对解决方案的澄清! - Mik
显示剩余3条评论

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