2009-02-11 78 views
8

我有一个将列出新闻文章的页面。为了减少页面的长度,我只想显示一个预告片(文章的前200个字/ 600个字母),然后显示一个“更多...”链接,点击后,将展开其余部分文章以jQuery/Javascript的方式。现在,我已经想通了,甚至在一些粘贴页面上找到了下面的帮助方法,它可以确保新闻文章(字符串)不会在一个词​​的中间被切碎:rails:获取文章的传情/摘录

def shorten (string, count = 30) 
    if string.length >= count 
     shortened = string[0, count] 
     splitted = shortened.split(/\s/) 
     words = splitted.length 
     splitted[0, words-1].join(" ") + ' ...' 
    else 
     string 
    end 
    end 

我遇到的问题是我从数据库中获得的新闻文章正文是格式化的HTML。所以如果我不走运,上面的帮助者会在html标签的中间切断我的文章字符串,并在那里插入“more ...”字符串(例如在“”之间),这会在页面上损坏我的html 。

有没有办法解决这个问题,或者有一个插件可以用来从HTML字符串中生成摘录/抽搐吗?

回答

2

非常感谢您的回答!然而,在此期间,我偶然发现了jQuery HTML Truncator plugin,这完全符合我的目的,并将截断转移到客户端。它没有得到任何简单:-)

1

如果你不想在html元素中间分割,你将不得不编写更复杂的解析器。它将不得不记住它是否在<>块的中间,以及它是否在两个标签之间。

即使你这样做,你仍然会有问题。如果有人把整篇文章放到HTML元素中,由于解析器无法将它分割到任何地方,因为缺少结束标记。

如果有可能,我会尽量不要将任何标签放入文章或将其保留到不包含任何东西的标签(没有<div>等等)。这样,你将只需要检查,如果你是在一个标签,这是非常简单的中间:

def shorten (string, count = 30) 
    if string.length >= count 
     shortened = string[0, count] 
     splitted = shortened.split(/\s/) 
     words = splitted.length 
     if(splitted[words-1].include? "<") 
     splitted[0,words-2].join(" ") + ' ...' 
     else 
     splitted[0, words-1].join(" ") + ' ...' 
    else 
     string 
    end 
    end 
3

My answer here应该做的工作。最初的问题(由我问)是关于截断降价,但我最终将降价转换为HTML,然后截断,所以它应该工作。当然,如果你的网站获得很多流量,你应该缓存摘录(也许当创建/更新文章时,你可以将摘录存储在数据库中?),这也意味着你可以允许用户修改或输入自己的摘录

用法:

>> puts "<p><b><a href=\"hi\">Something</a></p>".truncate_html(5, at_end = "...") 
=> <p><b><a href="hi">Someth...</a></b></p> 

..和代码(从对方的回答复制):

require 'rexml/parsers/pullparser' 

class String 
    def truncate_html(len = 30, at_end = nil) 
    p = REXML::Parsers::PullParser.new(self) 
    tags = [] 
    new_len = len 
    results = '' 
    while p.has_next? && new_len > 0 
     p_e = p.pull 
     case p_e.event_type 
     when :start_element 
     tags.push p_e[0] 
     results << "<#{tags.last}#{attrs_to_s(p_e[1])}>" 
     when :end_element 
     results << "</#{tags.pop}>" 
     when :text 
     results << p_e[0][0..new_len] 
     new_len -= p_e[0].length 
     else 
     results << "<!-- #{p_e.inspect} -->" 
     end 
    end 
    if at_end 
     results << "..." 
    end 
    tags.reverse.each do |tag| 
     results << "</#{tag}>" 
    end 
    results 
    end 

    private 

    def attrs_to_s(attrs) 
    if attrs.empty? 
     '' 
    else 
     ' ' + attrs.to_a.map { |attr| %{#{attr[0]}="#{attr[1]}"} }.join(' ') 
    end 
    end 
end 
+0

哦,我喜欢你的,它修复了文本周围标签的问题 – LDomagala 2009-02-11 14:01:58

15

您可以使用组合和Truncate

truncate("And they found that many people were sleeping better.", 
    :omission => "... (continued)", :length => 15) 
# => And they found... (continued) 

我在做类似的任务,我有博客文章,我只是想表明一个快速的摘录。所以,在我看来,我只是做:

sanitize(truncate(blog_post.body, length: 150)) 

这剔除了HTML标签,给我的第一个150个字符,所以它的MVC友好视图进行处理。

祝你好运!

+5

这可能会起作用,但你应该清理,然后截断。如果您截断然后进行消毒,则可能会在HTML标记的中间截断并进行消毒,将使部分标记可见。 – 2011-12-14 19:55:49

1

我会清理HTML并提取第一句。假设你有一篇文章的模式,与包含HTML中的 '身体' 属性:

# lib/core_ext/string.rb 
class String 
    def first_sentence 
    self[/(\A[^.|!|?]+)/, 1] 
    end 
end 

# app/models/article.rb 
def teaser 
    HTML::FullSanitizer.new.sanitize(body).first_sentence 
end 

这将转换“< b>此</b>是一个< EM>重要</em>的文章,!这里是文章的其余部分。“变成“这是一篇重要的文章”。

+0

猴子修补字符串为此有点过分... – DGM 2014-09-29 15:14:18

0

我解决了这个用以下解决方案

安装宝石 '消毒'

gem install sanitize 

,并使用下面的代码,在这里身体是包含文本的HTML标签。

<%= content_tag :div, Sanitize.clean(truncate(body, length: 200, separator: ' ', omission: "... #{ link_to '(continue)', '#' }"), Sanitize::Config::BASIC).html_safe %> 

给出了有效的HTML摘录。 我希望它可以帮助别人。

0

现在有一款名为HTMLTruncator的宝石可以帮您照顾到。我用它来显示文章摘录等,而且非常强大。