获取除不可选择元素之外的选定文本

Posted

技术标签:

【中文标题】获取除不可选择元素之外的选定文本【英文标题】:Get selected text except for un-selectable elements 【发布时间】:2016-03-17 08:29:30 【问题描述】:

我正在尝试获取文章中所选内容的开头和结尾的字符偏移量。但是,我希望在此过程中忽略一些元素。

在我的应用程序中,在 JS 动态注入一些元素之前,服务器正在处理 html,并且文本中的字符数在服务器和客户端之间保持一致非常重要。

我希望它会像window.getSelection()user-select: none; 一样简单。遗憾的是,虽然文本看起来没有被选中,但它仍然包含在所选内容的范围内。

我在下面写了一个简短的例子。我曾尝试编写removeFromSelection 作为解决方法,但没有取得多大成功。也许我需要删除与.unselectable 重叠的范围,并用全新的范围对象手动填充空白。我觉得这应该比我做的容易。我应该怎么做?

function findAncestorOffset(container, node, offset)

	if (node == container)
		return offset;
	var parent = node.parentNode;
	var syblings = parent.childNodes;
	for (var i = 0, len = syblings.length; i < len; i++)
	
		if (syblings[i] == node)
			break;
		offset += $(syblings[i]).text().length;
	
	return findAncestorOffset(container, parent, offset);


function removeFromSelection(selector, selection)

	$(selector).each(function(i, node)
		var range = document.createRange();
		range.selectNodeContents(node);
		selection.removeAllRanges(range);
	);


var onSelect = function()
  var sel = window.getSelection();
  //removeFromSelection('.unselectable', sel);
  var text = sel.toString();
  $('#out').text(text);
  var range = sel.getRangeAt(0).cloneRange();
  var i = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset);
  $('#from').text(i);
  $('#to').text(i + text.length);

$('.article').mouseup(onSelect);
$('.article').focusout(onSelect);
.unselectable 
	-webkit-touch-callout: none;
	-webkit-user-select: none;
	-khtml-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;


pre, .article 
  border: solid 1px black;
  padding: 12px;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Select This</h1>
<div class="article">
<p>
  This can be selected.
</p>
<p class="unselectable">
  This can't.
</p>
<p>
  And this can again.
</p>
</div>

<h1>Output</h1>
<div>
  From: <span id="from"></span>, To: <span id="to"></span>
</div>
<pre id="out">
</pre>

【问题讨论】:

你应该得到选定的 HTML:***.com/questions/5222814/… 然后你可以过滤掉它,例如:jsfiddle.net/xpca4fp5 但这需要更多的测试,因为我认为这可能是错误的 【参考方案1】:

嗯,这不是一个完整的解决方案,但它是一个开始。这个想法是遍历每个段落元素,如果是unselectable,则将其从选择中删除。我将选择转换为字符数组并在开头填充它,以便索引与整个文档中的索引匹配。

我没有注意最后的 tofrom 值,它们可能不正确 - 但更重要的是,如果您选择整个文本,您可以看到它在第二个不可选择的块。我没有时间摆弄它,但也许其他人可以从我离开的地方继续。

function findAncestorOffset(container, node, offset)

	if (node == container)
		return offset;
	var parent = node.parentNode;
	var syblings = parent.childNodes;
	for (var i = 0, len = syblings.length; i < len; i++)
	
		if (syblings[i] == node)
			break;
		offset += $(syblings[i]).text().length;
	
	return findAncestorOffset(container, parent, offset);


var onSelect = function()
  var sel = window.getSelection();

  var textArray = sel.toString().split('');
  var range = sel.getRangeAt(0).cloneRange();
  var from = findAncestorOffset($('.article')[0], range.startContainer, range.startOffset);
  var to = from + textArray.length;
  textArray = (new Array(from)).concat(textArray);
  
  var i = 0;
  $('.article p').each((_, rawElement) => 
    var element = $(rawElement);
    var sectionStart = i;
    var lengthOfSection = element.text().length;
    if(element.hasClass('unselectable')) 
      textArray.splice(sectionStart, lengthOfSection);
     else 
      i += lengthOfSection;
    
  );
  
  var text = textArray.join('');
  
  $('#from').text(from);
  $('#to').text(to);
  $('#out').text(text);

$('.article').mouseup(onSelect);
$('.article').focusout(onSelect);
.unselectable 
	-webkit-touch-callout: none;
	-webkit-user-select: none;
	-khtml-user-select: none;
	-moz-user-select: none;
	-ms-user-select: none;
	user-select: none;


pre, .article 
  border: solid 1px black;
  padding: 12px;
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<h1>Select This</h1>
<div class="article">
<p>
  This can be selected.
</p>
<p class="unselectable">
  This can't.
</p>
<p>
  And this can again.
</p>
<p>
  This can be selected.
</p>
<p class="unselectable">
  This can't.
</p>
<p>
  And this can again.
</p>
</div>

<h1>Output</h1>
<div>
  From: <span id="from"></span>, To: <span id="to"></span>
</div>
<pre id="out">
</pre>

【讨论】:

以上是关于获取除不可选择元素之外的选定文本的主要内容,如果未能解决你的问题,请参考以下文章

Xpath:选择除具有特定属性的元素之外的所有元素

如何选择除最后一个子元素之外的元素的所有子元素?

如何使用 Javascript 获取选定文本中的所有 HTML 选择标签?

jQuery 获取除子元素 X 之外的子元素的 HTML

选择除 $(this) 之外的所有内容

选择除 div 之外的所有元素不适用于 jQuery