contenteditable 文本选择不起作用
contenteditable selection of text not working
我面临以下问题:当我尝试在 contenteditable
元素中 select 文本并且 selection 的末尾是元素内容的开始时,没有select 事件已触发,并且没有 Selection
和 Range
个对象。
有人可以就为什么会发生这种情况或如何防止这种情况给我任何建议吗?
负责获取select离子范围的代码:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
JSFiddle(打开浏览器控制台以确保 selection 不会被记录)。
问题是您仅在 contenteditable
元素上发生特定事件时才记录选择更改。更具体地说,您有
$('div[contenteditable="true"]').bind("mouseup keyup touchend", // ...
特别是 mouseup
事件通常会在选择更改时触发。除非没有。当您在可编辑 div
之外释放鼠标(您在示例中这样做!),那么 div
将永远不会收到 mouseup
事件,因此永远不会记录选择。
有两种解决方法:
- 监听整个
body
上的事件。缺点是您会收到更多不影响选择的事件,并且仍然有可能在页面外收到 mouseup
个事件。
- 监听
selectionchange
事件。
document.addEventListener('selectionchange', function(event) {
console.log(event.type);
});
<div contenteditable="true">Hello world!</div>
您当然仍然可以像当前在此事件处理程序中那样访问选择。每次选择更改时都会触发此事件,因此您可能希望 throttle 它。
可以在下面找到完整的实现。
function handler() {
// do whatever you want here
// this shows the selection and all ranges it consists of
var sel = window.getSelection(),
ranges = Array(sel.rangeCount).fill(0).map((_, i) => sel.getRangeAt(i));
ranges = ranges.map((r) => `${r.startOffset}-${r.endOffset}`).join(';');
console.log(`Selection [${ranges}:"${sel.toString()}"]`);
}
function throttle(func) {
var timeoutId = false,
called = false,
wrap = function() {
if (!called) {
clearInterval(timeoutId);
timeoutId = false;
} else {
func();
}
called = false;
};
return function() {
if (timeoutId === false) {
func();
timeoutId = setInterval(wrap, 500);
} else {
called = true;
}
};
}
document.addEventListener('selectionchange', throttle(handler));
<div contenteditable="true">Hello world!</div>
您的实际代码可以完美运行并在控制台中记录一个 Selection 对象,即使选择的结束是元素内容的开始。
确实,您需要记录此 Selection
文本而不是记录整个 object
,它反映了每个事件的整个选择对象更改。
我更新了您的代码段以使用 Selection.toString()
记录选择的文本,您可以在此处看到它的工作原理:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel.toString()); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
您可以查看 this answer,它显示并解释了获取文本选择的完美方式。
我面临以下问题:当我尝试在 contenteditable
元素中 select 文本并且 selection 的末尾是元素内容的开始时,没有select 事件已触发,并且没有 Selection
和 Range
个对象。
有人可以就为什么会发生这种情况或如何防止这种情况给我任何建议吗?
负责获取select离子范围的代码:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
JSFiddle(打开浏览器控制台以确保 selection 不会被记录)。
问题是您仅在 contenteditable
元素上发生特定事件时才记录选择更改。更具体地说,您有
$('div[contenteditable="true"]').bind("mouseup keyup touchend", // ...
特别是 mouseup
事件通常会在选择更改时触发。除非没有。当您在可编辑 div
之外释放鼠标(您在示例中这样做!),那么 div
将永远不会收到 mouseup
事件,因此永远不会记录选择。
有两种解决方法:
- 监听整个
body
上的事件。缺点是您会收到更多不影响选择的事件,并且仍然有可能在页面外收到mouseup
个事件。 - 监听
selectionchange
事件。
document.addEventListener('selectionchange', function(event) {
console.log(event.type);
});
<div contenteditable="true">Hello world!</div>
您当然仍然可以像当前在此事件处理程序中那样访问选择。每次选择更改时都会触发此事件,因此您可能希望 throttle 它。
可以在下面找到完整的实现。
function handler() {
// do whatever you want here
// this shows the selection and all ranges it consists of
var sel = window.getSelection(),
ranges = Array(sel.rangeCount).fill(0).map((_, i) => sel.getRangeAt(i));
ranges = ranges.map((r) => `${r.startOffset}-${r.endOffset}`).join(';');
console.log(`Selection [${ranges}:"${sel.toString()}"]`);
}
function throttle(func) {
var timeoutId = false,
called = false,
wrap = function() {
if (!called) {
clearInterval(timeoutId);
timeoutId = false;
} else {
func();
}
called = false;
};
return function() {
if (timeoutId === false) {
func();
timeoutId = setInterval(wrap, 500);
} else {
called = true;
}
};
}
document.addEventListener('selectionchange', throttle(handler));
<div contenteditable="true">Hello world!</div>
您的实际代码可以完美运行并在控制台中记录一个 Selection 对象,即使选择的结束是元素内容的开始。
确实,您需要记录此 Selection
文本而不是记录整个 object
,它反映了每个事件的整个选择对象更改。
我更新了您的代码段以使用 Selection.toString()
记录选择的文本,您可以在此处看到它的工作原理:
$('div[contenteditable="true"]').bind("mouseup keyup touchend", function() {
lastCaretIndex = getSelectionRange();
});
function getSelectionRange() {
var sel;
if (window.getSelection) {
sel = window.getSelection();
console.log(sel.toString()); // this doesn't print anything event empty string
if (sel.rangeCount) {
return sel.getRangeAt(0);
}
} else if (document.selection) {
return document.createRange();
}
return null;
}
<div id="main-input" contenteditable="true">Hello world!</div>
<script type="text/javascript" src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
您可以查看 this answer,它显示并解释了获取文本选择的完美方式。