繁体   English   中英

焦点输入时保持选中文本

[英]Keep text selected when focus input

这个问题已经有人问过了,但直到现在还没有有效的答案,所以我很想再次打开它,希望我们能找到破解它的方法。

我有一个 contentEditable 段落和一个文本输入,当我选择一些文本并单击输入时,选择消失了。

所以我试图在输入 mousedown 上保存选择并将其恢复到 mouseup 上,是的,它可以工作(正如在 firefox 中预期的那样)但是......在 chrome 中,输入失去焦点:(

看到它在行动(使用 chrome ): https : //jsfiddle.net/mody5/noygdhdu/

这是我使用的代码:

HTML

<p contenteditable="true">
    Select something up here and click the input below
    <br> on firefox the input get the focus and the text still selected.
    <br> on chrome the text still selected but the input lose focus
</p>

    <input type="text" id="special" style="border: solid blue 1px">

javascript

function saveSelection() {
    if (window.getSelection) {
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) {
            return sel.getRangeAt(0);
        }
    } else if (document.selection && document.selection.createRange) {
        return document.selection.createRange();
    }
    return null;
}

function restoreSelection(range) {
    if (range) {
        if (window.getSelection) {
            sel = window.getSelection();
            sel.removeAllRanges();
            sel.addRange(range);
        } else if (document.selection && range.select) {
            range.select();
        }
    }
}


var specialDiv = document.getElementById("special");
var savedSel = null;

specialDiv.onmousedown = function() {

    savedSel = saveSelection(); // save the selection

};

specialDiv.onmouseup = function() {

    restoreSelection(savedSel); // restore the selection

};

由于我无法对 maioman 发表评论(需要一些声誉:)),因此对他的回答有一点补充:

它在 Firefox 中不起作用的原因是将焦点放在输入字段上会删除选择。

如果您在 p 上放置一个 mouseup 事件而不是在输入字段上放置一个焦点事件,则一切正常:

p.addEventListener('mouseup', () => {
      highlight(select()); // save the selection
    })

<span>替换 selection 可能是最简单的方法。 您还可以使用<iframe> ,这是谷歌在 Google Docs 中用来在单击 UI 元素时维护文档内文本选择的内容。

使用<span> ,解决方案可能是这样的(这个解决方案建立在你的原始代码和这里其他人的想法上,尤其是@Bekim Bacaj)。

 !function(doc, win) { var input = doc.getElementById('special') , editable = doc.getElementById('editable') , button = doc.getElementById('button') , fragment = null , range = null; function saveSelection() { if (win.getSelection) { sel = win.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } } else if (doc.selection && doc.selection.createRange) { return doc.selection.createRange(); } return null; } /* Not needed, unless you want also restore selection function restoreSelection() { if (range) { if (win.getSelection) { sel = win.getSelection(); sel.removeAllRanges(); sel.addRange(range); } else if (doc.selection && range.select) { range.select(); } } } */ function saveRangeEvent(event) { range = saveSelection(); if (range && !range.collapsed) { fragment = range.cloneContents(); toggleButton(); } } function toggleButton() { button.disabled = !fragment || !input.value.match(/^https?:.*/); } toggleButton(); editable.addEventListener('mouseup', saveRangeEvent); editable.addEventListener('keyup', saveRangeEvent); button.addEventListener('click', function(event) { // insert link var link = doc.createElement('a'); link.href = input.value; input.value = ''; range.surroundContents(link); toggleButton(); }); input.addEventListener('keyup', toggleButton); input.addEventListener('change', toggleButton); input.addEventListener('mousedown', function(event) { // create fake selection if (fragment) { var span = doc.createElement('span'); span.className = 'selected'; range.surroundContents(span); } }); input.addEventListener('blur', function(event) { // remove fake selection if (fragment) { range.deleteContents(); range.insertNode(fragment); //restoreSelection(); } fragment = null; }, true); }(document, window)
 .selected { background-color: dodgerblue; color: white; }
 <p id="editable" contenteditable="true"> Select something up here and click the input below <br>on firefox the input get the focus and the text still selected. <br>on chrome the text still selected but the input lose focus </p> <table> <tr> <td> <input type="text" id="special" style="border: solid blue 1px" placeholder="insert valid link incl. http://"> </td> <td> <button id="button">Add link</button> </td> </tr> </table>


链接到 jsFiddle

用 span 元素(并为其着色)替换您选择的区域可能是一种解决方法:

 var p = document.querySelector('p'); var old = p.innerHTML; var input = document.querySelector('input'); p.addEventListener('blur', () => { highlight(select()); // save the selection }) p.addEventListener('focus', () => { restore(); // restore the selection }) function highlight(r) { if (!r) return; var extracted = r.extractContents(); el = document.createElement('span'); el.appendChild(extracted); r.insertNode(el) } function select() { if (window.getSelection) { sel = window.getSelection(); if (sel.getRangeAt && sel.rangeCount) { return sel.getRangeAt(0); } } else if (document.selection && document.selection.createRange) { return document.selection.createRange(); } return null; } function restore() { p.innerHTML = old; }
 span { background: tomato; color:white; }
 <p contenteditable="true" tabindex="0"> Select something up here and click the input below <br> on firefox the input get the focus and the text still selected. <br> on chrome the text still selected but the input lose focus </p> <input type="text" id="special" style="border: solid blue 1px">

适用于 chrome,但不适用于 FF

正如埃里克所建议的那样,在p上使用mouseup事件(我实际上使用了blur )来调用highlight(select())将解决 FF 上的问题。

我在这个上做了一些工作......这是一个非常有趣和有指导意义的练习。
我主要从 Maioman 的回答开始。

我这样做是为了使选定的文本以输入字段中提供的 href 结尾,并且选定的文本在输入链接时保持选中状态。 这是我对你的问题的理解。

查看我的工作小提琴: https : //jsfiddle.net/Bes7weB/rLmfb043/
经测试可在 FF 46、Chrome 50、Safari 5.1 和 Explorer 11 上运行。

请注意,仅 IE10 及更高版本支持classList
此外,由于 mouseup 事件,链接不可“点击”。
但是您可以在鼠标悬停时看到 title 属性。
我假设您将保存段落的 innerHTML 以将其输出到其他地方。
;)


CSS:

a.highlighted {
  background: blue;
  color:white;
}

HTML:

<h1>Select some text below and click GO!</h1>
<br>
<p contenteditable="true" tabindex="0">
    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris nec risus turpis. Donec nisi urna, semper nec ex ac, mollis egestas risus. Donec congue metus massa, nec lacinia tortor ornare ac. Nulla porttitor feugiat lectus ut iaculis. In sagittis tortor et diam feugiat fermentum. Nunc justo ligula, feugiat dignissim consectetur non, tristique vitae enim. Curabitur et cursus velit. Etiam et aliquam urna. Duis pharetra fermentum lectus et fermentum. Phasellus eget nunc ultricies, ornare libero quis, porta justo. Sed euismod, arcu sed tempor venenatis, urna ipsum lacinia eros, ac iaculis leo risus ac est. In hac habitasse platea dictumst. Sed tincidunt rutrum elit, ornare posuere lorem tempor quis. Proin tincidunt, lorem ac luctus dictum, dui mi molestie neque, a sagittis purus leo a nunc.
</p><br>
<br>
<b>Add a link to selected text:</b> <input type="text" id="hrefInput" style="border: solid blue 1px" value="http://www.test.com"> <input type="button" id="gobutton" value="GO!"><br>
<span id="errorMsg" style="display:none;">No selected text!</span><br>
<input type="button" id="undoButton" value="Undo">

JavaScript:

var p = document.querySelector('p');
var old = p.innerHTML;
var HrefInput = document.getElementById("hrefInput");
var GoButton = document.getElementById("gobutton");
var UndoButton = document.getElementById("undoButton");
var errorMsg = document.getElementById("errorMsg");
var idCounter=0;
var textSelected=false;

UndoButton.addEventListener('focus', function() {
    console.log("Undo button clicked. Default text reloaded.");
    restore();
})

GoButton.addEventListener('click', function() {
    if(!textSelected){
        errorMsg.style.display="inline";
        errorMsg.style.color="rgb(166, 0, 0)";
        errorMsg.style.fontWeight="bold";
        return;
    }
    console.log("GO button clicked: Link id=a-"+idCounter+" created.");
    targetId="a-"+idCounter;
    document.getElementById(targetId).setAttribute("href",HrefInput.value);
    document.getElementById(targetId).classList.add("createdlink");
    document.getElementById(targetId).setAttribute("title",HrefInput.value);
    document.getElementById(targetId).classList.remove("highlighted");
    textSelected=false;
    idCounter++
})

p.addEventListener('focus', function() {
    errorMsg.style.display="none";
});

p.addEventListener('mouseup', function() {
    textSelected=true;
    console.log("Mouseup event in p : Text selected.");
    appendanchor(selectText()); // extract the selection
    HrefInput.focus();  // FireFox 
    HrefInput.blur();   // Needs it. Try without, you'll see.
})

function appendanchor(r) {  // onmouseup
    if (!r) return;
    extracted = r.extractContents();
    el = document.createElement('a');
    el.setAttribute("id", "a-"+idCounter);
    el.setAttribute("class", "highlighted");
    el.appendChild(extracted);
    r.insertNode(el)
}

function selectText() { // onmouseup
    if (window.getSelection) {
        console.log("window.getSelection");
        sel = window.getSelection();
        if (sel.getRangeAt && sel.rangeCount) { // Chrome, FF
            console.log(sel.getRangeAt(0));
            return sel.getRangeAt(0);
        }
        else{console.log(sel);}
    } else if (document.selection && document.selection.createRange) {
        console.log("elseif");
        return document.selection.createRange();
    }
    return null;
}

function restore() {
    p.innerHTML = old;
    textSelected=false;
}

在超时函数中添加焦点,这应该可以解决您的问题。

setTimeout(function(){
  document.getElementById("textToInsert").focus();
    }, 1);

jsfiddle: http : //jsfiddle.net/mody5/L5hx9h3k/1/

这是我很久以前在其他一些列表中作为答案发布的一个非常古老的片段。 它可能会帮助您重新思考当前的策略,并完全避免破坏自然预期的焦点行为的需要。

 function createLink(e){ if(e.target){ var a = window.getSelection().getRangeAt(0); var b = a.toString(); var z = document.createElement("span"); var l2 = prompt("Enter URL:", "http://"); b = b.link(l2); z.innerHTML=b; a.deleteContents(); a.insertNode(z) } else{ document.execCommand("CreateLink") } }
 <!DOCTYPE html> <html> <head> <title>Text to Hyperlink</title> </head> <body> <h1>Create a link</h1> Select some text and click the button. On the presented toolbox provide the url and confirm. The selected text will become a hyperlink<br> My Homepage<br> My Favorite<br> My Search Page<br><br> <button onclick="createLink(event)">Make it a link</button> <script> </script> </body> </html>

我给你一个提示,让你自己弄清楚。 您将需要检测是否正在使用 chrome。 在你的 jsfiddle 中,添加console.log(sel); sel = window.getSelection(); . 请注意日志中的选择在不同浏览器上是不同的。 老实说,我不确定为什么,但这可能会帮助您找出问题所在。

如果您将sel.removeAllRanges();注释掉,请注意同样的问题sel.removeAllRanges(); 你会得到一个错误,告诉你它们是不同的,如上所述。

用户选择:无

在选择后进行编辑操作的 UI 部分试试这个。 它对我有用,没有困难的解决方法,将当前选择保持为标有 this 的元素无法选择并更改或删除当前选择。

我想它不会回答你的问题但是,为了你的目的,我强烈建议你使用Angular.js或React.js。 如果你没有和他们一起玩,那么你将有一个学习曲线,但从长远来看,这将是非常值得的! (你也会发现更容易“选择”美容)

希望有帮助...... :)

在我的用例中,我能够在MutationObserver的帮助下解决在使用输入字段时丢失选择的问题。

在我的组件中,我获得了连接时初始化的范围的状态:

private range: Range | undefined;

componentWillLoad() {
    const selection: Selection | undefined = getSelection();

    this.range = selection?.getRangeAt(0);
}

getSelection是一个实用程序,它根据浏览器返回Selection

然后应用颜色的函数如下所示:

private selectColor($event: CustomEvent) {
    const selection: Selection | undefined = getSelection();

    if (!selection || !$event || !$event.detail) {
      return;
    }

    selection?.removeAllRanges();
    selection?.addRange(this.range);

    const observer: MutationObserver = 
      new MutationObserver( (_mutations: MutationRecord[]) => {
      observer.disconnect();

      this.range = selection?.getRangeAt(0);
    });

    const anchorNode: HTMLElement | undefined = getAnchorNode(selection);

    observer.observe(anchorNode, {childList: true});

    document.execCommand('foreColor', false, $event.detail.value);

发生了什么:我得到了选择,删除了所有范围并添加了我作为状态保留的范围。

然后我在选择锚节点上附加一个变异观察者。 为此,我使用了一个实用程序,它返回锚节点或其父节点,以防选择textcomment

然后我调用execCommand

一旦观察者启动,我就会查询新范围的选择(此时是文档的修改节点,而不是输入)并将其保存到我的状态。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM