タグを残して先頭からの表示文字数を指定する
デザイン担当者の要望で、HTMLのタグは残したまま表示する文字数を制限したいんだけど・・・という要望が。
いつも無茶ばっかだなあと思いながら実装方法はちょっと興味があったので考えてみた。
案1:正規表現。
→タグを消しちゃったらもとに戻せないので却下。
案2:1文字ずつループしながらチェック
→できれば避けたい
案3:DOMツリーを上から見ながら、text部分の文字数をカウント
→これでやってみる
以下、ソースです。
とりあえず作ったので無駄な部分が多いですが・・・
substr_with_tagの引数continue_linkは、続きを表示するためのリンクを渡します。
# HTMLタグは残したままで、指定した文字数を抽出します def substr_with_tag(str, word_cnt, continue_link) ret = "" # XMLにパース doc = REXML::Document.new str # 指定された文字数分表示されるように不要なエレメントを消す disp_txt = "" is_formated = format_xml(doc, disp_txt, word_cnt) ret = doc.to_s if is_formated ret = ret + continue_link end return ret end # substr_with_tagから再起呼び出しされてXMLを整形します def format_xml(xml_element, disp_txt, word_cnt) end_flg = false xml_element.elements.each do |e| if end_flg # 指定した文字数を超えた場合は要素を消す xml_element.delete e elsif e.type == REXML::Element # HTML要素の場合は再起呼び出し if !e.text.nil? disp_txt = disp_txt.concat(e.text) end if word_cnt <= disp_txt.split(//u).length end_flg = true # 指定文字数を超えたら、超過分をカット over_cnt = disp_txt.split(//u).length - word_cnt val = e.text.split(//u) e.text = val[0..(val.length - over_cnt)] else end_flg = format_xml(e, disp_txt, word_cnt) end elsif e.type == REXML::Text # Textの場合は表示される文字列に連結して、文字数チェック disp_txt = disp_txt.concat(e.value) if word_cnt <= disp_txt.split(//u).length end_flg = true # 指定文字数を超えたら、超過分をカット over_cnt = disp_txt.split(//u).length - word_cnt val = e.value.split(//u) e.value = val[0..(val.length - over_cnt)] end end end return end_flg end
確認してみると、うまくいく場合といかない場合がある。
リファレンスをよく見てみたら、REXML::Element#textは、最初の子テキストノードのvalueしか返さないらしい。
つまり、途中にbrタグやimgタグやaタグがあるとtextの途中までしかわからない。
textsというメソッドもあるけど、これだとテキストだけなので間のタグが取れない。
とりあえず一旦迷宮入りということで・・・
また余裕があったら考えよう