Jesse Hattabaugh
Jesse Hattabaugh

Reputation: 7966

Is there an HTML safe truncate method in Rails?

I have a string of HTML in Rails. I'd like to truncate the string after a certain number of characters not including the HTML markup. Also, if the split happens to fall in the middle of an opening and closing tag, I'd like to close the open tag/s. For example;

html = "123<a href='#'>456</a>7890"
truncate_markup(html, :length => 5) --> "123<a href='#'>45</a>"

Upvotes: 44

Views: 25524

Answers (11)

Vlad
Vlad

Reputation: 305

You can use the truncate method in combination with sanitize.

truncate(sanitize(html_content), length: 100, separator: '</p>', escape: false)

This will truncate your HTML using the separator but it can produce HTML without closing tags. To fix this we can use the sanitize method again, which will clean the HTML and add the missing tags.

sanitize(truncate(sanitize(html_content), length: 100, separator: '</p>', escape: false))

Upvotes: 2

Marcos R. Guevara
Marcos R. Guevara

Reputation: 6378

your_tagged_string.truncate(60).html_safe

Upvotes: 2

Vijay Chouhan
Vijay Chouhan

Reputation: 4883

We can do that with the help of simple_format http://api.rubyonrails.org/classes/ActionView/Helpers/TextHelper.html#method-i-simple_format

html = "123<a href='#'>456</a>7890"
simle_format(truncate_markup(html, :length => 5)) 

=> "123 456 7890"

Upvotes: 0

Asnad Atta
Asnad Atta

Reputation: 4003

This will help you without any extra effort

raw your_string.truncate(200)

Upvotes: 4

mastaBlasta
mastaBlasta

Reputation: 5850

You should solve this problem with CSS rather than Ruby. You are doing something that affects the DOM layout, and there is no way to programmatically devise a solution that will work consistently.

Let's say you get your HTML parser gem working, and you find a lowest common denominator character count that will work most of the time.

What happens if you change font sizes, or your site layout? You'll have to recalculate the character count again.

Or let's say your html has something like this in it: <p><br /></p><br /> That is zero characters, however it would cause a big chunk of blank text to be inserted. It could even be a <blockquote> or <code> tag with too much padding or margin to throw your layout totally out of whack.

Or the inverse, let's say you have this 3&nbsp;&#8773;&nbsp;&#955; (3 ≅ λ) That is 26 characters long, but for display purposes it is only 5.

The point being that character count tells you nothing about how something will render in the browser. Not to mention the fact HTML parsers are hefty pieces of code that can at times be unreliable.

Here is some good CSS to deal with this. The :after pseudo class will add a white fade to the last line of content. Very nice transition.

body { font-size: 16px;}
p {font-size: 1em; line-height: 1.2em}
/* Maximum height math is:
   line-height * #oflines - 0.4
   the 0.4 offset is to make the cutoff  look nicer */
.lines-3{height: 3.2em;}
.lines-6{height: 6.8em;}
.truncate {overflow: hidden; position:relative}
.truncate:after{
    content:""; 
    height: 1em; 
    display: block; 
    width: 100%; 
    position:absolute;
    background-color:white; 
    opacity: 0.8; 
    bottom: -0.3em
}

You can add as many .lines-x classes as you see fit. I used em but px is just as good.

Then apply this to your element: <div class="truncate lines-3">....lots of stuff.. </div>

and the fiddle: http://jsfiddle.net/ke87h/

Upvotes: 16

msanteler
msanteler

Reputation: 2183

the regular truncate function works fine, just pass :escape => false as an option to keep the HTML intact. eg:

truncate(@html_text, :length => 230, :omission => "" , :escape => false)

RubyOnRails.org

*Edit I didn't read the question very carefully (or at all TBH), so this answer does not solve this question... It IS the answer I happened to be looking for though, so hopefully it helps 1 or 2 people :)

Upvotes: 83

Maximus S
Maximus S

Reputation: 11095

Solving this problem from the client side:

view:

<script>
  $(function() {
    $('.post-preview').each(function() {
      var tmp_height = $(this).innerHeight();
      if ((tmp_height > 100) && (tmp_height < 200)) {
        $(this).addClass("preview-small");
      }
      else if (tmp_height >= 200) {
        $(this).addClass("preview-large")
      }
      else {
        //do nothing
      }
    });
  });
</script>

css

.preview-small {
  height: 100px;
  overflow: hidden;
}

.preview-large {
  height: 200px;
  overflow: hidden;
}

Upvotes: -2

priyanka Ukirde
priyanka Ukirde

Reputation: 139

You can use

truncate(html.gsub(/(<[^>]+>)/, ''), 5)

Upvotes: 1

gdelfino
gdelfino

Reputation: 11181

There are two completely different solutions both with the same name: truncate_html

  1. https://github.com/ianwhite/truncate_html : This is a gem and uses an html parser (nokogiri)
  2. https://github.com/hgmnz/truncate_html : This is a file you put in your helpers directory. It uses regular expressions and has no dependencies.

Upvotes: 36

jmanrubia
jmanrubia

Reputation: 1967

We had this need in zendone.com. The problem was that the existing solutions were very slow when truncating long HTML documents (MBs) into shorter ones (KBs). I ended up coding a library based in Nokogiri called truncato. The library includes some benchmarks comparing its performance with other libs.

Upvotes: 4

Mark
Mark

Reputation: 87

You could use the truncate_html plugin for this. It uses nokogiri and htmlentities gems and does exactly what the plugin name suggests.

Upvotes: 6

Related Questions