提问人:Lukas 提问时间:11/10/2023 更新时间:11/11/2023 访问量:61
双击 contenteditable 元素进行聚焦(看起来不那么容易)
Doubleclick contenteditable element to focus (not as easy at it seems)
问:
我一直在努力寻找解决这个问题的方法,所以我现在要在这里问。
从一个普通的 contenteditable div 元素开始,如下所示:
<div contenteditable>Edit this text</div>
如果您单击文本,您将能够使用光标在您单击的位置进行编辑。
现在,我不希望文本在第一次单击时是可编辑的,并且我不希望在元素未聚焦时与文本进行交互。我可以通过禁用 div、使用 CSS 或覆盖元素打开 contenteditable 来实现这一点。
我现在希望该元素在双击时再次可编辑,其行为与您正常单击该元素所期望的行为完全相同。让我们使用 JavaScript 和 EventListener 来实现 or 为此。onmousedown
ondblclick
示例 1
<div id="wrapper">
<div id="overlay"></div>
<div id="editable" contenteditable>Edit this text</div>
</div>
overlay.ondblclick = () => {
overlay.style.display = 'none';
editable.focus();
};
// use onblur to restore the overlay
这有效,但光标将位于元素的开头。如果不使用 ,则必须第三次单击以聚焦元素。focus()
示例 2
<div id="editable" contenteditable="false">Edit this text</div>
editable.ondblclick = () => {
editable.setAttribute('contenteditable', true);
};
// use onblur to set contenteditable to false again
// additional CSS will prevent being able to interact with the text if not focused
这是可行的,但您将选择您要单击的单词,而不是将文本光标放在那里。
这是针对私人项目的,可访问性、兼容性等无关紧要。
答:
0赞
slugcat-dev
11/11/2023
#1
经过几个小时的研究,我得出了这个解决方案:
const editable = document.getElementById('editable');
editable.ondblclick = (event) => {
if (document.activeElement === event.target) return;
editable.setAttribute('contenteditable', true);
selectRange(getMouseEventCaretRange(event));
}
function getMouseEventCaretRange(event) {
if (event.rangeOffset !== undefined) {
// The new fancy Firefox-only way that doesn't even have documentation
let range = document.createRange();
range.setStart(event.rangeParent, event.rangeOffset);
range.collapse(true);
return range;
} else if (document.caretPositionFromPoint) {
// The 'official' way (only used by Firefox) that doesn't even position the cursor correctly
// VSC keeps telling me the method doesn't exist
let pos = document.caretPositionFromPoint(event.clientX, event.clientY);
let range = document.createRange();
range.setStart(pos.offsetNode, pos.offsetX);
range.collapse(true);
return range;
} else if (document.caretRangeFromPoint) {
// The 'deprecated WebKit-proprietary fallback method' (used by every mf browser out there)
return document.caretRangeFromPoint(event.clientX, event.clientY);
}
}
function selectRange(range) {
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
* {
font-family: monospace;
}
#editable {
font-size: 1.5rem;
}
#editable[contenteditable="false"] {
user-select: none;
cursor: pointer;
}
#editable[contenteditable="false"]::selection {
background-color: transparent;
}
<div id="editable" contenteditable="false" onblur="this.setAttribute('contenteditable', false);">Double click to edit this text</div>
<ul>
<li>Caret is placed where clicked, not at the start</li>
<li>Double clicked word is not highlighted</li>
<li>Text can not be interacted with if the element isn't focused</li>
</ul>
它很长,很复杂,但它有效。在我看来,这方面的 API 完全搞砸了。我希望这能在未来帮助到某人。
0赞
JPC
11/11/2023
#2
如果您愿意在双击之前允许指针事件,用光标隐藏该功能,则无需任何复杂操作,只需在添加 contenteditable 时阻止双击操作,同时确保它处于焦点中,并且原始单击将失败。
document.addEventListener('mousedown', (event) => {
if (event.detail > 1) { // double-clicked
const e = event.target;
if (e.id === "content" && !['true', 'plaintext-only'].includes(e.contentEditable)) {
event.preventDefault();
e.style.cursor = 'auto';
e.contentEditable = true;
e.focus();
}
}
}, false);
#content {
cursor: default;
}
<div id="content">Edit this text</div>
评论