HTML上の要素表示・非表示をJavascriptだけで切り替える
SNSで流れてきた広告サイトから技術を勉強してみた記録の続き。
前回はこれ。
yamakisso.hatenablog.com
概略
永遠ちゃんねるという過去に存在してたネタページを元に勉強した成果だけ残す。
サンプルコードでページ遷移を再現
実際にサンプルコードを書いて、ページ遷移を再現してみる。
方法が色々思いつくが、模倣じゃなくて参考にして勉強するのが目的なので元ネタから見つかる方法も、自分で思いつく方法も両方確認した。
サンプルの動作期待値
動作の期待値は以下の通りとして、検証する。
- 入場ボタンが配置された簡易なページを表示しておいてボタンをクリックしたらページが書き換わることを期待する。
- 実際はページ遷移させずにJavascriptだけで制御する。
遷移前のHTML
<html lang='ja'> <meta charset="UTF-8"/> <title>出現/消去のてすとぺーじ</title> <link rel="stylesheet" type="text/css" href="./common/visible.css"/> <script type="text/javascript" src="./common/visible.js"></script> <body> <!-- DIVタグ:それ自身は特に意味を持たないが、ブロック要素として要素をまとめる場合に使う。 --> <!-- SPANタグ:DIVとほぼ同様。インライン要素としてまとめる場合に使う。 --> <div id="top" class="top-page"> <header> <div class="header">TOPページのヘッダです</div> </header> <div class="logo">ロゴ表示</div> <div class="warning"><p>表示非表示を制御するボタンです。</br>気が向いたらクリックしましょう。</p></div> <div class="login-line"><div onclick="login()" class="login-button">入場する</div></div> <footer> <div class="footer">TOPページのフッタです。このHTMLはテストで作りました。</div> </footer> </div> </body> </html>
遷移後のHTML
ボタンをクリックした後の状態。
「入場に成功しました!!」の文字列が表示される。
<html lang='ja'> <meta charset="UTF-8"/> <title>出現/消去のてすとぺーじ</title> <link rel="stylesheet" type="text/css" href="./common/visible.css"/> <script type="text/javascript" src="./common/visible.js"></script> <body> <div id="main" class="main"> <div class="text-main">入場に成功しました!!</div> </div> </body> </html>
サンプルCSS(visible.css)
@charset "UTF-8"; /* Topページ */ div.top-page { background-color: #EEFFFF; } /* ヘッダ */ div.header { width: 100%; height: 50px; margin: 0 0 10px; color: #ffffff; background-color: #999999; font-size: 20px; } /* ロゴ */ div.logo { text-align: center; margin: auto; padding: 10px 0 0 0; width: 300px; height: 600px; border: 1px solid #000000; background-color: #FFFFFF; } /* 注意書き */ div.warning { text-align: center; } /* ログインボタン */ div.login-line { text-align: center; } div.login-button { margin: auto; display: inline-block; padding: 0.5em 1em; text-decoration: none; background: #668ad8;/*ボタン色*/ color: #FFF; border-bottom: solid 4px #627295; border-radius: 3px; cursor: pointer; } /* フッタ */ div.footer { text-align: right; width: 100%; height: 50px; margin: 30px 0 0 0; background-color: #EEEEEE; font-size: 15px; } /* mainページ */ div.main { background-color: #000000; } div.text-main { color: #FFFFFF; font-size: 50px; }
ボタンデザインは外部参考:
CSSで作る!押したくなるボタンデザイン100(Web用)
初心者が適当に定義したデザインと差がありすぎて浮いてる…
実現方針1:(非推奨なやつ)HTML要素の有無を動的に切り替える
HTML上の要素有無を書き替えてしまう方法。
元ネタはHTML上にベース部分があり、この方法は取っていない。
個人的にこちらの方針はイマイチだと思う。理由は後述。
1-1. ()HTML要素を追加/削除する
素直な方法。
非表示にするHTML要素を削除し、表示するHTML要素を追加してやる。
こんな感じでJavaScriptを作って、操作前HTMLに対して動作させる。
・visible.js
function login() { // Top要素の削除を実施 // 1. TopページのDiv要素を取得 var topDom = document.getElementById('top'); // 2. Topページの親要素(body)を取得 var bodyDom = topDom.parentNode; // 3. TopページのDiv要素を削除 bodyDom.removeChild(topDom); // Main要素の作成と追加 var mainDiv = document.createElement('div'); mainDiv.id = 'main'; mainDiv.classList.add('main'); bodyDom.appendChild(mainDiv); // text-mainの作成と追加 var textMainDiv = document.createElement('div'); textMainDiv.classList.add('text-main'); textMainDiv.textContent = "入場に成功しました!!"; mainDiv.appendChild(textMainDiv); }
1-1. がダメな理由
HTML内の一部、局所的に書き換えるならこの方法でも良いと思うが、ページ遷移など中~大規模に切り替えるには懸念がある。
HTMLの記述がJavaScriptのコードに内包されてしまい、最終的に出力されるページの構成が見通せなくなってしまうという点。
正直、このサンプルですら出力したいHTMLの構成を読み取るのは面倒。
テンプレートエンジンなどに要素の追加削除を任せられる場合でもなければ、この方法での大規模な要素切り替えは不適切だと思う次第。
1-2. (非推奨なやつ)HTML上でコメントアウト/コメントアウト解除する
無駄に捻くれた方法だと思う。
コメントアウトしたテンプレートを仕込んでおいて、動的にコメントアウト化する部分を切り替える。
まず操作前HTMLはこうする。
<html lang='ja'> <meta charset="UTF-8"/> <title>出現/消去のてすとぺーじ</title> <link rel="stylesheet" type="text/css" href="./common/visible.css"/> <script type="text/javascript" src="./common/visible.js"></script> <body> <div id="top" class="top-page"> <header> <div class="header">TOPページのヘッダです</div> </header> <div class="logo">ロゴ表示</div> <div class="warning"><p>表示非表示を制御するボタンです。</br>気が向いたらクリックしましょう。</p></div> <div class="login-line"><div onclick="login()" class="login-button">入場する</div></div> <footer> <div class="footer">TOPページのフッタです。このHTMLはテストで作りました。</div> </footer> </div> <div id="main" class="main"> <!-- <div class="text-main">入場に成功しました!!</div> --> </div> </body> </html>
で、visible.jsをこうする。
function login() { // 1. TopページのDiv要素を取得してコメントアウト var topDom = document.getElementById('top'); topDom.innerHTML = '<!--' + topDom.innerHTML + '-->'; // 2. Main要素を取得してコメントアウトを解除 var mainDom = document.getElementById('main'); var newHtml = mainDom.innerHTML ; // コメントアウトの先頭を削除 newHtml = newHtml.replace(/<!--/, ''); // コメントアウトの末尾を削除 newHtml = newHtml.substr(0, newHtml.lastIndexOf('-->')); // 要素を置き換え mainDom.innerHTML = newHtml; }
1-2. がダメな理由
書く前にわかってたけどこれはない。
利用部分をコメントで制御するってのが直感的じゃないし、操作のサポートもなくて無理やり組み込んでるし。
コメントはちゃんとコメントのために使うべき。
実現方針2:「表示しない」スタイルに切り替えて制御する
CSSで「表示しない」ためのスタイルを作って、そのスタイルを追加/削除することで制御する。
まず、操作対象とするHTMLはこれ。
<html lang='ja'> <meta charset="UTF-8"/> <title>出現/消去のてすとぺーじ</title> <link rel="stylesheet" type="text/css" href="./common/visible.css"/> <script type="text/javascript" src="./common/visible.js"></script> <body> <div id="top" class="top-page"> <header> <div class="header">TOPページのヘッダです</div> </header> <div class="logo">ロゴ表示</div> <div class="warning"><p>表示非表示を制御するボタンです。</br>気が向いたらクリックしましょう。</p></div> <div class="login-line"><div onclick="login()" class="login-button">入場する</div></div> <footer> <div class="footer">TOPページのフッタです。このHTMLはテストで作りました。</div> </footer> </div> <div id="main" class="main hidden"> <div class="text-main">入場に成功しました!!</div> </div> </body> </html>
でもって、visible.jsはこれ。
function login() { // 1. Topページのスタイルにhiddenを追加 document.getElementById('top').classList.add('hidden'); // 2. Mainページのスタイルからhiddenを削除 document.getElementById('main').classList.remove('hidden'); }
CSSのスタイル"hidden"を定義することで、非表示を実現することになる。
2-1. CSSのdisplay: none で制御する
非表示で調べるとぱっと出てくるのはこれか。
display: none
のスタイルで表示しないという制御が可能。
visible.cssに下記を追加して実現する。
/* hiddenの設定 */ div.hidden { display: none; }
CSS - displayの定義
設定値 | 下記いずれか inline, block, list-item, inline-block, table, inline-table, table-row-group, table-header-group, table-footer-group, table-row, table-column-group, table-column, table-cell, table-caption, none, inherit |
---|---|
初期値 | inline |
適用先 | 全要素 |
継承 | 継承されない |
設定値は多いので、代表的なものとnoneの説明のみ。
- block
- ブロック要素として縦に配置する。
縦横のpadding
,margin
とwidth
,height
を指定可能。※ 要素内側の余白がpaddingで、外側の余白がmargin。 - inline
- 行内で配置する。
横のpadding
やmargin
を指定できるが、width
やheight
は指定不可。 - inline-block
- 行内でブロック要素として配置する。
横に配置されるがブロックなので、縦横のpadding
,margin
とwidth
,height
を指定可能。 - none
- 要素が表示されないようにする。
2-2. CSS:opacity(不透明度)で透明にする
要素の不透明度を設定できる。これで透明にすれば非表示という考え方。
「永遠ちゃんねる」はdisplay
とこれの合わせ技っぽい。
サンプルに適用したときは透明にしただけだと配置場所がずれたので、無理やり大きさを0にする設定を入れることになった。何かいい方法があるのかな…
あと、クリックイベントが残るので無効化も必要。
visible.cssに下記を追加して実現。
/* hiddenの設定 */ div.hidden { opacity: 0.0; width 0 !important; height: 0 !important; pointer-events: none; /* クリック無効化 */ }
CSS - opacityの定義
設定値 | アルファ値(0.0~1.0) |
---|---|
初期値 | 1(不透明) |
適用先 | 全要素 |
継承 | 継承されない |
要素の不透明度を0.0(透明)~1.0(不透明)の間で定義できる。基本的には半透明にして表示を重ねたい場合に使う認識。
非表示のためだけにこれを使うメリットは…思いつかなかった。
クリックイベントが残る、Ctrl+A
の選択範囲に入るといった特徴があるが、透明なのにそれらの挙動が残るとユーザ操作として直感的ではない気がしてしまう。
SEOやら広告的意味やら、自分が調査しきれてないレベルでは何かメリットがあるのかもしれない。
2-3. CSS:positionの設定で画面外に追いやる
要素の出力位置を絶対指定にして、ありえない負数を入力することで画面外に追いやる方法でも、見た目の非表示は実現できる。
visible.cssに下記を追加して実現。
/* hiddenの設定 */ div.hidden { position: absolute; top: -10000px; left: -10000px; }
CSS - positionの定義
多分このあたりに載ってる:CSS Positioned Layout Module Level 3
詳細調査は機会があれば。
まあ、非表示を実現するために使うのは避けたほうがいいと思う。
実現できるかどうかが要素の大きさに依存するし、単純にソース読んでこれが出てきたらなんで?って疑問に思う。