How I can truncate text with jQuery but keep the HTML? Tried with the following code but unfortunately doesn't return the HTML formatting.
var truncate = function() {
$('p.truncated').text(function(index, oldText) {
if (oldText.length > 20) {
return '...' + oldText.substr(-25);
}
return oldText;
});
}
If you run the snippet you will see what I am seeking.
var truncate = function() {
$('p.truncated').text(function(index, oldText) {
if (oldText.length > 20) {
return '...' + oldText.substr(-25);
}
return oldText;
});
}
truncate();
var truncate2 = function() {
$("p.truncated2").each(function() {
//var theContent = $(this).text();
var theContent = $(this).html();
var n = theContent.substr(-25);
$(this).html( '...' +n );
});
}
truncate2();
p {
margin: 0 0 30px 0;
display: block;
padding: 5px;
}
streak {
color: green;
}
double {
color: orange;
}
p span {
color: grey;
}
<script src=".1.1/jquery.min.js"></script>
How to make something like this...
<p class="sheet">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
...bees something like this with jquery?
<p>
...
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
<hr />Here is a try with jquery but it return text without html elements.
<p class="truncated">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
The second try
<p class="truncated2">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
How can I keep html elements?
How I can truncate text with jQuery but keep the HTML? Tried with the following code but unfortunately doesn't return the HTML formatting.
var truncate = function() {
$('p.truncated').text(function(index, oldText) {
if (oldText.length > 20) {
return '...' + oldText.substr(-25);
}
return oldText;
});
}
If you run the snippet you will see what I am seeking.
var truncate = function() {
$('p.truncated').text(function(index, oldText) {
if (oldText.length > 20) {
return '...' + oldText.substr(-25);
}
return oldText;
});
}
truncate();
var truncate2 = function() {
$("p.truncated2").each(function() {
//var theContent = $(this).text();
var theContent = $(this).html();
var n = theContent.substr(-25);
$(this).html( '...' +n );
});
}
truncate2();
p {
margin: 0 0 30px 0;
display: block;
padding: 5px;
}
streak {
color: green;
}
double {
color: orange;
}
p span {
color: grey;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
How to make something like this...
<p class="sheet">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
...bees something like this with jquery?
<p>
...
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
<hr />Here is a try with jquery but it return text without html elements.
<p class="truncated">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
The second try
<p class="truncated2">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
How can I keep html elements?
Share
Improve this question
edited Jun 3, 2015 at 6:10
Paul
asked Jun 3, 2015 at 3:31
PaulPaul
2,6982 gold badges19 silver badges28 bronze badges
5 Answers
Reset to default 2If you want to keep counting character count, you can use range
object also. You set the range you want to delete and it'll delete nodes as well. So you manipulate text but you keep html
tags.
var truncate = function() {
var range_all = document.createRange();
range_all.selectNodeContents(document.getElementsByClassName('truncated')[0]);
if (range_all.endOffset > 20) {
range_all.setEnd(document.getElementsByClassName('truncated')[0], range_all.endOffset - 12);
range_all.deleteContents();
$('p.truncated').html('... ' + $('p.truncated').html());
}
}
truncate();
var truncate2 = function() {
$("p.truncated2").each(function() {
//var theContent = $(this).text();
var theContent = $(this).html();
var n = theContent.substr(-25);
$(this).html('...' + n);
});
}
truncate2();
p {
margin: 0 0 30px 0;
display: block;
padding: 5px;
}
streak {
color: green;
}
double {
color: orange;
}
p span {
color: grey;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
How to make something like this...
<p class="sheet">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
...bees something like this with jquery?
<p>
...
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
<hr />If text is longer than 25 character, select until 12th and delete selection. Then add '...' at the beginning.
<p class="truncated">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
The second try
<p class="truncated2">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
How can I keep html elements?
This is a classic dilemma for any CMS or blog, where the teaser should present the begin of an article: often the solution is either stripping text from its tags and cut at a precise count OR keep tags but cut approximatively because the tags are counted too...
Here is my suggestion for a way to satisfy both exigences (keep tags AND cut at exact count):
function cutKeepingTags(elem, count) {
var matches = $(elem).html().match(/([^<])*(<.*>)?(.*)/),
// To be more clear, let's "rename" what we get:
textBefore = matches[1] || '',
children = matches[2] || '',
textAfter = matches[3] || '';
if (textBefore.length <= count) {
// cool, already that's end!
return textBefore.substr(count);
}
if ($(elem).text().length == textBefore.length + textAfter.length) {
/* (remember that .text() returns the concatenation of all text nodes found
deeply in the element)
If equal there is no child (or there are children but no text in them),
so we can simply keep children and text enough after children:
*/
return textBefore + chidren + textAfter.substr(count - textBefore.length);
}
// Otherwise we must loop through children to get more text:
var grabText = textBefore;
$(elem).children().each(function() {
// Get text from current child:
grabText += cutKeepingTags(this, count - grabText.length);
if (grabText.length == count) {
// We already got text enough:
return grabText;
}
});
// No more child and count not reached, return what we got.
return grabText;
}
Not tested!
Edit:
Finally got a functional solution: you find it here on Code Review.
You'll want to split the child elements first.
var truncate = function() {
var $elem = $('p.truncated');
var html = $elem.html().split(/>\s*</);
if(html.length > 5)
html = "... <" + html.slice(-6).join('> <');
$elem.html(html);
}
truncate();
p {
margin: 0 0 30px 0;
display: block;
padding: 5px;
}
streak {
color: green;
}
double {
color: orange;
}
p span {
color: grey;
}
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p class="truncated">
<streak>start</streak>
<streak>1</streak>
<streak>2</streak>
<double>3</double>
<double>4</double>
<span>5</span>
<span>6</span>
<streak>7</streak>
<streak>8</streak>
<double>9</double>
<double>10</double>
<streak>end</streak>
</p>
Mate, replace your
var theContent = $(this).html();
with
var theContent = $(this).outerHTML();
I've done my own pure JS (TypeScript actually) implementation based on the code posted above. It uses different approach on extracting parent's html and has additional functionality to trim spaces at the beginning of the provided html and always trims whitespace in the end, if the text is being truncated
/**
* recursively looks through all the text nodes until the text size exceeds the limit
* in that case will trim the html preserving all the existing tags
* @param element html element to be processed
* @param maxLength max amount of text symbols to be present
* @param shouldTrimStart set to false to preserve whitespace at the beginning of the projected content.
*/
function truncateHTML<T extends ChildNode>(element?: T, maxLength = 0, shouldTrimStart = true) {
let truncatedHTML = '';
let innerTextLength = 0;
const nodes = Array.from(element?.childNodes ?? []);
for (let index = 0; index < nodes.length; index++) {
const node = nodes[index];
switch (node.nodeType) {
case Node.TEXT_NODE: {
let content = (node.nodeValue ?? '').substr(0, maxLength - innerTextLength);
if (shouldTrimStart) {
content = content.trimLeft();
if (content) {
shouldTrimStart = false;
}
}
truncatedHTML += content;
innerTextLength += content.length;
break;
}
case Node.ELEMENT_NODE: {
const childPart = truncateHTML(node, maxLength - innerTextLength, shouldTrimStart);
const clone = node.cloneNode(true) as Element;
if (innerTextLength + childPart.innerTextLength === maxLength && index === nodes.length - 1) {
// removing trailing spaces in the end of the last word
childPart.truncatedHTML = childPart.truncatedHTML.trimRight();
}
clone.innerHTML = childPart.truncatedHTML;
truncatedHTML += clone.outerHTML;
innerTextLength += childPart.innerTextLength;
shouldTrimStart = childPart.shouldTrimStart;
break;
}
default:
console.error(new Error(`Unexpected node type: ${node.nodeType}`));
}
if (innerTextLength === maxLength) {
break;
}
}
return { truncatedHTML, innerTextLength, shouldTrimStart };
}