如何在保留HTML的同时截断元素的文本内容?
How can I truncate the text contents of an Element while preserving HTML?
我意识到这里有几个类似的问题,但没有一个答案能解决我的问题。
我需要能够将元素的innerHTML
截断为给定的字符长度,并考虑到任何内部HTML元素的文本内容,并保留所有HTML标签。
我已经找到了几个很好的解决这部分问题的答案,以及几个插件,它们都能做到这一点。
然而,在所有情况下,解决方案将直接截断任何内部元素的中间,然后关闭标签。
在我的例子中,我需要所有内部标签的内容保持完整,基本上允许任何"将被"截断的内部标签超过给定的字符限制。
任何帮助都将是非常感激的。
编辑:例如: This is an example <a href="link">of a link</a> inside another element
包含空格的长度为51个字符。如果我想将其截断为23个字符,我们必须缩短</a>
标记内的文本。这正是大多数解决方案所做的。
这将得到以下内容:
This is an example <a href="link">of a</a>
然而,对于我的用例,我需要保持任何剩余的可见标记完全完整,并且不以任何方式截断。
所以对于上面的例子,当尝试截断为23个字符时,我想要的最终输出如下: This is an example <a href="link">of a link</a>
所以本质上我们是在检查截断发生在哪里。如果它位于元素的外部,我们可以将HTML字符串拆分为该长度。另一方面,如果元素中是元素,则移动到该元素的结束标记,对所有父元素重复此操作,直到返回到根字符串并将其拆分。
听起来您希望能够截断HTML字符串的长度作为文本字符串,例如考虑以下HTML:
'<b>foo</b> bar'
在本例中,HTML长度为14个字符,文本长度为7个字符。您希望能够将其截断为X文本字符(例如2),以便新的HTML现在是:
'<b>fo</b>'
说明:我的答案使用了我开发的一个库
你可以使用HTMLString库- Docs: GitHub.
库使这个任务非常简单。要使用HTMLString截断我们上面概述的HTML(例如截断为2个文本字符),您需要使用以下代码:
var myString = new HTMLString.String('<b>foo</b> bar');
var truncatedString = myString.slice(0, 2);
console.log(truncatedString.html());
EDIT: After additional information from OP.
下面的truncate函数截断到最后一个完整的标记,并满足嵌套标记。
function truncate(str, len) {
// Convert the string to a HTMLString
var htmlStr = new HTMLString.String(str);
// Check the string needs truncating
if (htmlStr.length() <= len) {
return str;
}
// Find the closing tag for the character we are truncating to
var tags = htmlStr.characters[len - 1].tags();
var closingTag = tags[tags.length - 1];
// Find the last character to contain this tag
for (var index = len; index < htmlStr.length(); index++) {
if (!htmlStr.characters[index].hasTags(closingTag)) {
break;
}
}
return htmlStr.slice(0, index);
}
var myString = 'This is an <b>example ' +
'<a href="link">of a link</a> ' +
'inside</b> another element';
console.log(truncate(myString, 23).html());
console.log(truncate(myString, 18).html());
这将输出:
This is an <b>example <a href="link">of a link</a></b>
This is an <b>example <a href="link">of a link</a> inside</b>
你已经标记了你的问题regex
,但是你不能可靠地用正则表达式做到这一点。的链接。所以innerHTML
出局了
如果你真的在谈论字符,我认为除了循环遍历元素内的节点,递归到后代元素,加上你发现的文本节点的长度之外,没有其他方法可以做到这一点。当找到需要截断的点时,截断该文本节点,然后删除后面的所有节点 —或者更好的方法是,将文本节点分成两部分(使用splitText
),并将后半部分移动到display: none span中(使用insertBefore
),然后将所有后续文本节点移动到display: none span中。
虽然HTML是臭名昭著的可怕的格式和边缘情况,不受正则表达式的影响,这里有一个超级轻的方法,你可以轻松地处理HTML与嵌套标签在香草JS。
(function(s, approxNumChars) {
var taggish = /<[^>]+>/g;
var s = s.slice(0, approxNumChars); // ignores tag lengths for solution brevity
s = s.replace(/<[^>]*$/, ''); // rm any trailing partial tags
tags = s.match(taggish);
// find out which tags are unmatched
var openTagsSeen = [];
for (tag_i in tags) {
var tag = tags[tag_i];
if (tag.match(/<[^>]+>/) !== null) {
openTagsSeen.push(tag);
}
else {
// quick version that assumes your HTML is correctly formatted (alas) -- else we would have to check the content inside for matches and loop through the opentags
openTagsSeen.pop();
}
}
// reverse and close unmatched tags
openTagsSeen.reverse();
for (tag_i in openTagsSeen) {
s += ('<''' + openTagsSeen[tag_i].match(/'w+/)[0] + '>');
}
return s + '...';
})
简而言之:截断它(忽略一些不可见的字符),regex匹配标记,将打开标记推入堆栈,并在遇到关闭标记时弹出堆栈(再次假设格式良好);然后关闭所有仍然打开的标签。
(如果您想实际获得一定数量的可见字符,您可以保持一个运行计数器,显示到目前为止看到的非标记字符的数量,并在达到配额时停止截断。)
免责声明:你不应该把它作为一个生产解决方案,但如果你想要一个超轻的,个人的,黑客的解决方案,这将得到基本的格式良好的HTML。
因为它是盲的和词法的,这个解决方案错过了很多边缘情况,包括应该不关闭的标签,如<img>
,但是您可以硬编码这些边缘情况,或者,您知道,如果您想要,为真正的HTML解析器包含一个库。幸运的是,由于HTML格式很差,您不会看到它;)
感谢T.J.我很快意识到,要高效率地完成这一工作,唯一的方法就是使用本地DOM方法并遍历元素。
我已经编写了一个快速、相当优雅的函数来完成这个任务。
function truncate(rootNode, max){
//Text method for cross browser compatibility
var text = ('innerText' in rootNode)? 'innerText' : 'textContent';
//If total length of characters is less that the limit, short circuit
if(rootNode[text].length <= max){ return; }
var cloneNode = rootNode.cloneNode(true),
currentNode = cloneNode,
//Create DOM iterator to loop only through text nodes
ni = document.createNodeIterator(currentNode, NodeFilter.SHOW_TEXT),
frag = document.createDocumentFragment(),
len = 0;
//loop through text nodes
while (currentNode = ni.nextNode()) {
//if nodes parent is the rootNode, then we are okay to truncate
if (currentNode.parentNode === cloneNode) {
//if we are in the root textNode and the character length exceeds the maximum, truncate the text, add to the fragment and break out of the loop
if (len + currentNode[text].length > max){
currentNode[text] = currentNode[text].substring(0, max - len);
frag.appendChild(currentNode);
break;
}
else{
frag.appendChild(currentNode);
}
}
//If not, simply add the node to the fragment
else{
frag.appendChild(currentNode.parentNode);
}
//Track current character length
len += currentNode[text].length;
}
rootNode.innerHTML = '';
rootNode.appendChild(frag);
}
这可能会得到改进,但从我最初的测试来看,它非常快,可能是由于使用了本机DOM方法,它似乎完美地为我完成了这项工作。我希望这对其他有类似需求的人有所帮助。
免责声明:以上代码将只处理一级深的 HTML标签,它不会处理标签内的标签。不过,通过跟踪节点父节点并将节点附加到片段中的正确位置,可以很容易地对其进行修改。就目前而言,这对我的需求很好,但对其他人可能没用。
- 识别切换条元素文本并在量角器中单击它
- 数组函数不适用于从元素文本创建的JavaScript数组
- 将元素文本替换为子项中的值
- 如何在不丢失格式的情况下连续淡入() 元素文本的每个字符,包括嵌套元素
- 使用 jQuery 从目标页面而不是当前页面获取元素文本
- 使用jquery为html元素文本的每个字符设置动画
- regex替换元素文本
- 在 Firefox 中单击鼠标时无法选择所有输入元素文本
- 如何确定 HTML 元素文本中“新行”的位置
- 在量角器中将元素文本转换为对象
- 无法使用 jquery 获取 xml 元素文本值
- jQuery - 仅获取父元素文本,而不获取子元素文本
- 单击其他元素(文本输入)时显示下拉列表
- 使用自定义绑定更新元素文本
- Android驱动程序的scrollTo和scrollToExact 方法的问题.方法不会滚动到所需的元素文本,而是滚动
- 选择元素文本并添加逗号
- 获取不带 html 标记的页面元素文本,但保留换行符
- 删除元素文本中的最后一个字符
- 如何按顺序获取所有元素文本
- 使用AngularJS将元素文本复制到title属性的干净方法