0

I'm currently having a problem. I have a div containing html. But if the html inside the div is too long, I want it to be shortened and three dots added in the end. Now with plain text this would be no problem. With html my problem is this. Lets say i have

<div>
    Some <a href="someurl">LinkText</a> and a 
    <span class="this-class">special formatting that's also <b>bold</b></span>
</div>

Now if i implement this with

$(theSelector).html($(theSelector).html().substring(0,50));

will lead to something like this (I didn't count the exact length ;))

<div>
    Some <a href="someurl">LinkText</a> and a 
    <span class="this-

so i will end up with corrupt html. The easy way to solve this would be just to use the text and not the html but i want to keep the formatting. Can anyone think of a way to nicely shorten this but also close all tags again correctly?

Bhuwan
  • 16,525
  • 5
  • 34
  • 57
relief.melone
  • 3,042
  • 1
  • 28
  • 57

2 Answers2

4

Your best bet is not to solve this at the HTML/JavaScript level at all. Instead, use CSS's overflow: hidden and text-overflow: ellipsis (you may also need white-space: nowrap):

.limit {
  width: 20em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
<div class="limit">
    Some <a href="someurl">LinkText</a> and a 
    <span class="this-class">special fomatting that's also <b>bold</b>
    </span>
</div>

Sadly it will happily add the ellipsis in the middle of a word (see this question), but your JavaScript would as well, so...

But if for some reason you can't do that and you do actually need to modify the DOM, I would do it by working through the text nodes, not working at an HTML level:

function applyEllipsis(target, chars) {
  var i, child, text;
  // Loop through child nodes, counting off characters from `chars` until we've
  // accounted for them all
  i = 0;
  while (i < target.childNodes.length) {
    child = target.childNodes[i];
    if (chars <= 0) {
      // Remove any remaining nodes
      target.removeChild(child);
    } else {
      switch (child.nodeType) {
        case Node.TEXT_NODE:
          // Normalize whitespace
          child.nodeValue = child.nodeValue.replace(/[\s\r\n]+/g, " ");
          if (child.nodeValue.length >= chars) {
            // We're all done, trim and add the ellipsis
            child.nodeValue = child.nodeValue.substring(0, chars) + "\u2026";
            chars = 0;
          } else {
            // Not done yet, subtract the number we've seen
            chars -= child.nodeValue.length;
          }
          break;
        case Node.ELEMENT_NODE:
          // Recurse
          chars = applyEllipsis(child, chars);
          break;
      }
      
      // Move to next child
      ++i;
    }
  }
  return chars;
}
applyEllipsis(document.querySelector(".limit"), 50);
<div class="limit">
    Some <a href="someurl">LinkText</a> and a 
    <span class="this-class">special fomatting that's also <b>bold</b>
    </span>
</div>

You may have to tweak that a bit but it should get you going the right way. In particular, it counts a single whitespace character at the beginning of a block display element (e.g., between <div class="limit"> and Some) which won't be displayed as a space by the browser.

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
0

Try like this, using regular expression

var regex = /(<([^>]+)>)/ig
var html = jQuery("#container").html();
var result = html.replace(regex, "");
console.log(result.substr(0,50));
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
    Some <a href="someurl">LinkText</a> and a 
    <span class="this-class">special fomatting that's also <b>bold</b>
    </span>
</div>
Sourabh Somani
  • 2,138
  • 1
  • 13
  • 27