HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

Truncating text with jQuery but keep the HTML formatting

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
formattingthetruncatingwithtextbutkeepjqueryhtml

Problem

I repeat here this answer on Stack Overflow.

I first posted an answer with not finalized code, as a simple description of the solution I could think, without any test. But later I remained interested, so I worked to make it (hopefully) perfectly functional.

To precisely define what it is meant to do, let me cite my previous answer:


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 approximately because the tags are counted too...

So here the intent is to take an HTML element with any number of children, and any nesting level and return:

  • the "same" element (i.e. keeping its tag and attributes)



  • where resulting text content (i.e. visible as characters in the resulting page) is limited to a given count



  • where resulting text is built from successive text nodes in their natural order



  • where encountered tags are keeped intact and at their natural place



Here is my actual solution:

function cutKeepingTags(elem, reqCount) {
  var grabText = '',
      missCount = reqCount;
  $(elem).contents().each(function() {
    switch (this.nodeType) {
      case Node.TEXT_NODE:
        // Get node text, limited to missCount.
        grabText += this.data.substr(0,missCount);
        missCount -= Math.min(this.data.length, missCount);
        break;
      case Node.ELEMENT_NODE:
        // Explore current child:
        var childPart = cutKeepingTags(this, missCount);
        grabText += childPart.text;
        missCount -= childPart.count;
        break;
    }
    if (missCount == 0) {
      // We got text enough, stop looping.
      return false;
    }
  });
  return {
    text:
      // Wrap text using current elem tag.
      elem.outerHTML.match(/^]+>/m)[0]
      + grabText
      + '',
    count: reqCount - missCount
  };
}


And here is a working example. (I kept the HTML example posted by the previous

Solution

First of all, I would just like to say that this is a really good and useful function. From a Code Review standpoint, there are almost no errors in it that I know of. Here are a few things I found from examining it:

Keep spacing uniform

Line 8 doesn't have a space between parameters, where your other function calls do. This is most likely due to quick typing and not any major issue other than a cleanliness nitpick.

An invalid input for if

Lines 19-21 where you have an if like so:

if (missCount == 0) {
  // We got text enough, stop looping.
  return false;
}


You should never use == over === due to it being possible that something like "0" would match the same as 0. This is because the double equals signs finds if it matches an exact value, where triple equals signs tests for exact value and type. So your final code should look like this for the if statement:

if (missCount === 0) {
  // We got text enough, stop looping.
  return false;
}


Optional - JSHint

If you use JSHint in JSFiddle, then you'll run into errors when trying to run this:

elem.outerHTML.match(/^]+>/m)[0]
  + grabText
  + '',


If you're worried about that then you just have to write it all on one line. But for being short and concise, that might not be what you want.

Code Snippets

if (missCount == 0) {
  // We got text enough, stop looping.
  return false;
}
if (missCount === 0) {
  // We got text enough, stop looping.
  return false;
}
elem.outerHTML.match(/^<[^>]+>/m)[0]
  + grabText
  + '</' + elem.localName + '>',

Context

StackExchange Code Review Q#92801, answer score: 6

Revisions (0)

No revisions yet.