ナウい（死語）MARQUEE（死語）を考えてみる

## MARQUEEタグとは

MARQUEEタグは、大昔に廃止されたタグです。

> この機能は廃止されました。まだいくつかのブラウザーで動作するかもしれませんが、いつ削除されてもおかしくないので、使わないようにしましょう。

[MDN &lt;marquee&gt;: マーキー要素 (廃止)](https://developer.mozilla.org/ja/docs/Web/HTML/Element/marquee)より


これは昔に繁華街に溢れていた電光掲示板よろしく、
要素内の文字を永遠にスクロールさせながら流し続ける、という代物です。

このタグは非推奨になったのは**htmlで修飾を行ってはいけない**（修飾はCSSに任せろ）というHTML5の基本理念からです。

そこで、MARQUEEタグの機能を継承しつつ、HTML5の理念にも沿った、ナウい（死語）マーキーを作ってみたいと思います。

## ナウいマーキーの仕様

### シームレスにループさせる

元祖のMARQUEEタグは基本的に左まで文字を流したあと、何も表示されない時間を挟んで、また右端から文字が現れる、という仕様でした。
これだと何も表示していない時間が寂しいので、常に文字が表示されているように、シームレスなループに変更します。

### 文字をループさせるためだけに文字を要素に直接流し込まない。

簡単にシームレスなループを作る方法として、要素内の文字列を二重にする方法が考えられます。

例えば「アイウエオ」を「アイウエオアイウエオ」として、-50%左方向にアニメーションすればそれで完成なのですが、修飾のためだけに重複した文字列を作るのは良くないので、これを疑似要素を使って実現するようにします。

### アニメーションを停止できるようにする。

W3C（のWCAG2.0）によれば、アニメーションされるコンテンツは停止する方法をユーザーに提供するべき、だそうなので、マーキーにマウスを乗せるとアニメーションが一時停止するようにします。

### 文字幅によってアニメーションが発動するかどうかを判断する

文字が１行に収まるのなら、何も無理にマーキーさせる必要はありません。
なので、jsで文字がはみ出すかどうかを判断し、はみ出す時だけアニメーションするようにします。

## ナウいMARQUEEのコーディング

```html:HTML
<div class="marq" data-marquee="">
  <p class="marq__inner">Lorem ipsum dolor sit amet, consectetur adipisicing elit. Est sint recusandae illo voluptatibus hic rem magnam error eius enim, nobis perferendis cupiditate vel. Quo reprehenderit.</p>
</div>
```

HTMLはいたって普通です。
二重構造で、cssで利用することになる、data-marquee属性を空で指定しておきます。

```scss:SCSS
.marq {
  background-color: #f00;
  color: #fff;
  white-space: nowrap;
  width: 100%;
  height: 30px;
  display: flex;
  flex-flow: row nowrap;
  justify-content: flex-start;
  align-items: center;
  overflow: hidden;

  &::after {
    content: '';
  }

  &__inner {
    width: auto;
    min-width: 100%;
    text-align: center;
  }

  &::after,
  &__inner {
    padding: 0 10px;
    display: inline-block;
    white-space: nowrap;
    width: auto;
    flex-grow: 0;
    flex-shrink: 0;
    box-sizing: border-box;
  }

  &.is-marquee {
    &::after {
      content: attr(data-marquee);
    }

    &::after,
    & .marq__inner {
      animation: marqMove 5s linear 0s infinite normal;
    }
  }
  &:hover {
    &::after,
    & .marq__inner {
      animation-play-state: paused;
    }
  }
}

@keyframes marqMove {
  0% {
    transform: translate(0);
  }
  100% {
    transform: translate(-100%);
  }
}
```

scss(css)は少し複雑です。   
自動折り返しをなくすためにflexを利用しています。
ポイントとしては`::after`疑似要素と`.marq__inner`要素が全く同じ見た目になるようにしています。
また、自要素の幅を100%として使える`translate`を使ってアニメーションするようにしています。
`::after`疑似要素と`.marq__inner`要素を同じ量だけ左に動かすことで、シームレスな動きに見えるようにできます。
また`animation-play-state: paused;`でマウスオーバーでアニメーションが止まるようにしています。

```javascript:javascript
Array.prototype.forEach.call(
  document.querySelectorAll('.marq'),
  item => {
    const itemInner = item.querySelector('.marq__inner');
    const itemText = itemInner.innerText;
    item.dataset.marquee = itemText;
    ['load', 'resize'].forEach(handle => window.addEventListener(handle, () => {
      if (this.innerWidth < itemInner.clientWidth) {
        item.classList.add('is-marquee');
      } else {
        item.classList.remove('is-marquee');
      }
    }));
  },
);
```

javascriptもそんなに複雑ではありません。やっていることは基本的に２つです。

まず、`.marq`要素のdata-marquee属性に中のテキストを挿入しています（`::after`疑似要素で利用するため）。
また、ロードイベント、リサイズイベントを監視し、ページ幅より、`.marq__inner`要素が大きい時だけ`.is-marquee`クラスを付与します。
これによって、アニメーションの有無を決定し、cssのmarqMoveアニメーションが動くようになります。


## ナウいデモ

<p class="codepen" data-height="265" data-theme-id="0" data-default-tab="css,result" data-user="ichimonzi" data-slug-hash="KEwLod" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid black; margin: 1em 0; padding: 1em;" data-pen-title="PUSEUDO MARQUEE">
  <span>See the Pen <a href="https://codepen.io/ichimonzi/pen/KEwLod/">
  PUSEUDO MARQUEE</a> by ichimonzi (<a href="https://codepen.io/ichimonzi">@ichimonzi</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

複数のマーキーを設置できるように書いたので、文字列の長さが違うマーキーを複数設置してみました。
ウィンドウをリサイズすれば、ウィンドウ幅と文字列の長さによってマーキーの状態が変わるのがわかると思います。

### 結論

**ナウくしてもマーキーはやはりウザい。**

改善したい点として、

+ `::after`疑似要素だと中にタグ入れられないから結局は普通にdomクローンして挿入したほうがいいんじゃないの？
+ `animation-duration`が一定だと、長い文字列が入った時流れが超速い
+ 文字列応じてdurationの秒数を制御して同じ速さにしたい
+ 一時停止じゃなくてクリックで完全に止めたい

などいろいろありますが、とりあえず満足はしました。

### 余談

ちなみに、overflow時の動作を決める、`overflow-style`というプロパティの値として`marquee`が提唱されたことがあったようですが、
**2019年現在、overflow-styleプロパティに対応しているブラウザは確認できませんでした。**