LoginSignup
2

More than 5 years have passed since last update.

riotでjQueryを使わない複数行の三点リーダの実装

Last updated at Posted at 2018-09-21

背景

長い文字列は末尾を三点リーダに変換したい
例えばECサイトとかで商品が一覧で並んでいるときに、
長ーい商品名のアイテムだけ高さがずれたらダサい。

僕.「cssだけでできる?」
G .「IEは対応してないけど良い?」
僕.「(IEからも割とアクセスされえるので)だめ」
※Gはgoogle検索

僕.「ではscriptで対応するか・・・。」
G .「これなんか参考にならんか? あとこれとか」
僕.「JQuery使ってるやつばっかやん。」
僕.「うちJQuery使ってないねん。」

僕.「あー。あとriot使ってるんでそっちで対応したい」
G .「該当ありません」
僕.「ぐぬぬ」

条件

  • 表示する文字列が長い時に、描画領域で特定の高さになるように末尾を省略して三点リーダにして表示する
  • riotjs使っているのでパーツをriotのtagで用意したい
  • IEでも動く
  • jQueryは使わない

今回は上記縛りから色々苦労した。

実装方法

ellipsis.tag
<ellipsis>
  <p class="{opts.class}">
    {text}
  </p>

  <script>
    let tag = this
    tag.text = this.opts.text

    // DOMが生成されてから実行するため
    this.on('mount', function(){
      // riotはthis.rootでエレメントにアクセスできる
      let element = this.root.querySelector('p');

      // 計算用エレメントの用意
      let clone = element.cloneNode(true)
      clone.style.position = 'absolute'
      clone.style.overflow = 'visible'
      clone.style.height = 'auto'
      clone.style.width = document.defaultView.getComputedStyle(element, null).width

      this.root.appendChild(clone)

      let html = element.innerHTML
      let height = parseFloat(document.defaultView.getComputedStyle(element, null).height)
      let shorten_cnt = 0

      // cloneの高さが用意された領域に収まるまで文字をカットしていく
      while((html.length > 0) && (parseFloat(document.defaultView.getComputedStyle(clone, null).height) > height)) {
        html = html.substr(0, html.length - 1);
        clone.innerHTML = html + '...';
        // 文字列が長すぎた場合ループ回数が増えすぎるので保険として
        if (shorten_cnt > 1000) {
          break;
        }
        shorten_cnt++
      }

      // cloneは計算にしか使わないので削除
      this.root.removeChild(clone)

      // 最初から高さ内に収まっている場合は何もしない
      if (shorten_cnt > 0) {
        tag.text = html + '...'
        tag.update()
      }
    })

  </script>
</ellipsis>

呼ぶところ

<ellipsis text="眠い時には何をやっても眠いので、いっそお昼寝すればいいと思うのですが、会社で大ぴらに眠ることも流石に気がひけるのでタイピングしながら眠る術を身につけた(嘘" class="height_40"></ellipsis>

こうしておけば長さに合わせて三点リーダーが挿入される

<p class="height_40">眠い時には何をやっても眠いので、いっそお昼寝すればいい... </p>

 詰まりポイント説明

this.on('mount')

riotマウント後でないとDOMが生成されてないので、mount時に呼ばれる関数内に処理を書く。

本家にも書いてた

cloneNode

単に

let clone = element

ってしてしまうと参照渡しになってしまうのでcloneNode(true)でディープコピー

display:noneは?

参考サイトにはcloneしたものをdisplay:noneすべし、ってなっていますが、
riotの場合、heightがautoのままになって計算できなくなってしまうみたいなので外した。
どうせ処理後に消されるので(もしかしたらちらつくかも)

parseFloat

これ使わないと
document.defaultView.getComputedStyle(clone, null).height
が「20px」とかの文字列となり、比較できないのでFloat型に変換。
数値になるように「px」とかいった無用な文字列も削除してくれるようだ。

参考

以下、参考にさせていただきました。
ありがとうございます。

意外と需要がある(泣)文字数を省略して「...」にしたい。複数行のとき

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2