JavaScript获取选中文本:换行符数量不正确问题求助
处理浏览器选中文本换行符不一致的问题
问题背景
我使用以下代码获取浏览器中用户选中的文本,后续用于处理和API调用:
核心插件代码
(function(e, t) { "use strict"; var n = t.selection; var i = t.getSelection; var o = i || n; var r = function(e) { var t = Object.prototype.toString.call(e); return typeof e === "object" && /^\[object (HTMLCollection|NodeList|Object)\]$/.test(t) && e.hasOwnProperty("length") && (e.length === 0 || typeof e[0] === "object" && e[0].nodeType > 0) }; var c = function(e, t) { var n; return function() { var i = this, o = arguments; var r = function() { n = null; e.apply(i, o) }; clearTimeout(n); n = setTimeout(r, t) } }; var s = function(t, n) { this.element = t; this.callback = n || function() {}; this.isTouch = "ontouchstart" in e; this.hasLib = e.jQuery && t instanceof e.jQuery || e.Zepto && t instanceof e.Zepto }; s.prototype = { events: function() { var e = this.callback; var t = this.getText; this[this.isTouch ? "bindTouch" : "bindMouseUp"](function() { e(t()) }) }, getText: function() { var n = ""; if (i) { n = e.getSelection().toString() } else if (t.selection && t.selection.type !== "Control") { n = t.selection.createRange().text } return n }, checkForSelections: function(e, t, n) { var i; var o = function(e) { var n = t(); var i = setInterval(function() { if (t() !== n) { e(t()); n = t() } else if (t() === "") { clearInterval(i) } }, 100) }; var r = function() { e.removeEventListener("touchend", c, false); e.addEventListener("touchend", c, false); if (i) { clearInterval(i) } i = setInterval(function() { var e = t(); if (e !== "") { n(e); c(); o(n) } }, 100) }; var c = function() { clearInterval(i); e.removeEventListener("touchend", c, false) }; e.addEventListener("touchstart", r, false) }, bindTouch: function(e) { var t = this.checkForSelections; var n = this.getText; if (this.hasLib) { this.element.each(function() { t(this, n, e) }); return } var i = function(i) { t(i, n, e) }; if (!r(this.element)) { i(this.element); return } [].forEach.call(this.element, function(e) { i(e) }) }, bindMouseUp: function(e) { if (this.hasLib) { this.element.on("mouseup", c(e, 150)); return } var t = function(t) { t.addEventListener("mouseup", c(e, 150), false) }; if (!r(this.element)) { t(this.element); return } [].forEach.call(this.element, function(e) { t(e) }) } }; e.selecting = function(e, t) { if (!o) { return } new s(e, t).events() } })(window, document);
调用代码
var myElement = document.body; window.selecting(myElement, function(selector) { var text = selector; instance.publishState("text", text); });
实际使用中发现,返回的文本换行符数量和用户实际选中的不一致:
用户选中的文本:
Hello this is a test Hello this is another test代码返回的文本:
Hello this is a test Hello this is another test
这种不一致完全破坏了依赖准确换行符数量的功能。
问题原因
- 浏览器原生的
getSelection().toString()方法在处理HTML内容时,会把块级元素(比如<p>、<div>)之间的分隔转换成多个换行符,这是因为块级元素在页面上默认有上下间距,浏览器提取文本时会将这些间距映射为额外换行。 - 原代码直接使用该方法获取文本,没有处理HTML结构带来的额外换行,导致和视觉上的换行数量不匹配。
解决方案
需要手动遍历选中范围的DOM节点,提取文本并根据节点类型正确处理换行,替代原生的toString()方法。
修改后的getText方法
将原插件中的getText方法替换为以下代码:
getText: function() { var selection = window.getSelection(); if (!selection.rangeCount) return ""; var range = selection.getRangeAt(0); var fragment = range.cloneContents(); var textNodes = []; // 遍历片段中的所有文本节点 var walk = document.createTreeWalker(fragment, NodeFilter.SHOW_TEXT, null, false); var node; while (node = walk.nextNode()) { textNodes.push(node); } var text = ""; var prevNode = null; textNodes.forEach(function(node) { // 如果前一个节点是块级元素,添加一个换行 if (prevNode && isBlockElement(prevNode.parentNode)) { text += "\n"; } // 添加当前文本节点的内容 text += node.textContent; // 如果当前节点的父元素是块级元素,添加一个换行 if (isBlockElement(node.parentNode)) { text += "\n"; } prevNode = node; }); // 去除首尾多余的换行,合并连续的多个换行 return text.replace(/^\n+|\n+$/g, "").replace(/\n{2,}/g, "\n\n"); } // 辅助函数:判断元素是否为块级元素 function isBlockElement(element) { if (!element || element.nodeType !== Node.ELEMENT_NODE) return false; var blockElements = ["P", "DIV", "H1", "H2", "H3", "H4", "H5", "H6", "UL", "OL", "LI", "BLOCKQUOTE", "PRE"]; return blockElements.includes(element.tagName); }
关键逻辑说明
- 遍历文本节点:通过
TreeWalker获取选中范围内的所有文本节点,避免直接提取时的格式混乱。 - 块级元素处理:遇到块级元素的文本节点时,添加单个换行符,模拟视觉上的换行效果。
- 清理多余换行:用正则表达式去除首尾多余换行,并将连续多个换行合并为两个(对应段落间的分隔)。
效果验证
修改后,选中的文本会和视觉上的换行数量一致,比如之前的例子会正确返回:
Hello this is a test Hello this is another test
内容的提问来源于stack exchange,提问作者NL97
相关产品推荐
相关产品推荐

