选中文本段落首尾p元素ID时focusNode返回undefined的问题排查
嘿,这个问题我之前在做文本选区相关功能的时候也踩过一模一样的坑!下面给你拆解原因和解决办法:
问题原因分析
这个差异是WebKit/Blink内核浏览器(Chrome、Safari、Opera)和Firefox对选区结束位置的DOM节点解析逻辑不同导致的:
- 当你选中的文本刚好结束在
<p>标签的最后一个字符之后(也就是段落末尾的位置),Chrome这类浏览器会把选区的focusNode指向<p>元素的父节点(比如<body>)里的空白文本节点(就是段落之间的换行/空格产生的隐藏节点)。这时候你直接取focusNode.parentNode得到的是<body>,自然没有id属性,所以返回undefined。 - 而Firefox会把
focusNode保留在<p>元素内部的文本节点上,所以parentNode就是对应的<p>元素,能正确获取到id。
解决方法
解决思路是不直接依赖当前节点的父节点,而是通过DOM向上查找最近的带id的<p>元素。jQuery的.closest('p[id]')方法正好能满足这个需求——它会从当前节点开始向上遍历DOM树,找到第一个匹配带id属性的p元素这个条件的节点。
修改后的完整代码如下:
$(document).on('keydown', function ( e ) { if ((e.metaKey ) && (String.fromCharCode(e.which).toLowerCase() === 'c') ) { var selectedText = document.getSelection(); // 修正选中文本的判断逻辑:Selection对象本身不能直接和空字符串比较 if (selectedText && selectedText.toString().trim() !== '') { // 从anchorNode向上找最近的带id的p元素 var selectedBeginId = $(selectedText.anchorNode).closest('p[id]').attr('id'); // 从focusNode向上找最近的带id的p元素 var selectedEndId = $(selectedText.focusNode).closest('p[id]').attr('id'); alert(selectedBeginId + ' - ' + selectedEndId); } else { alert('No passage selected'); } } });
额外优化(可选)
如果用户存在从后往前选文本的情况(也就是选区的起始和结束位置反过来),你还可以加个判断,确保输出的id顺序是选区实际的起止顺序:
// 在获取两个id之后添加这段逻辑 if (selectedText.anchorOffset > selectedText.focusOffset) { // 交换两个id,保证顺序正确 [selectedBeginId, selectedEndId] = [selectedEndId, selectedBeginId]; }
内容的提问来源于stack exchange,提问作者Leo
相关产品推荐
相关产品推荐

