NaaN日記

やったこと、覚えたことを発信する場

Element.nextElementSiblingとElement.previousElementSiblingを使って表示する画像を切り替えた話

こんばんは

甘口です。
先日投稿した2018年を振り返る記事でも少し触れましたが、
最近、画像投稿Webアプリケーションの作成に取り組んでいます。
qiita.com
github.com
今回は、その中でも個人的に気に入っている、
`Element.nextElementSibling``Element.previousElementSibling`
2つを使って、表示する画像を切り替えた話をします。
画像の表示の切り替えに焦点を絞って話すので、
実際に実装している機能の殆どについては省略します。

また、作成しているWebアプリケーションではFlaskを用いていますが、
ここではHTML、JavaScriptCSSのみで動作確認をしたものを用います。

画像を表示する

CSSはこのようになっています。
[static/css/styles.css]

デフォルト
@charset "UTF-8";

body{
    background: #D2EFFA;
}

img{
    max-width: 180px;
    max-height: 180px;
    border: 10px solid #cccccc;
}
画像がクリックされたとき
img.table{
   display: inline-block;
   width: auto;
   height: auto;
   max-width: 100%;
   max-height: 100%;
   border: none;
}

.pic-table {
    display: none; /*画像がクリックされるまで非表示に*/
    background: #1d5acdaa; 
    position: fixed;
    text-align: center;
    right: 10px;
    overflow: hidden;
    padding-top: 100px;
    top: 0;
    bottom: 0;
    right: 0;
    left: 0;
}

f:id:CNaan:20181224004712p:plain
複数の画像が並んでいる画像

はじめに、上の画像をクリックすると、画面中央に画像が表示されるようにします。
[pic.html]

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>画像置き場</title>
    <link rel="stylesheet" href="static/css/styles.css">
</head>
<body>
<div id="picTable" class="pic-table"></div>
<div id="alterAlbum"></div>

<script src="static/js/main.js"></script>
</body>

(id)alterAlbumで表示されている画像を(id)picTableで表示させます。

なお、alterAlbumは次のように、複数のimgの子ノードを持っているものとします。

<div id="alterAlbum">
    <img src="static/pic/christmas_dance_tonakai.png">
    <img src="static/pic/inoshishi_chototsu_moushin.png">
    <img src="static/pic/present_motsu_woman.png">
</div>

[static/js/main.js]

//画像がクリックされたことを伝える関数
function linePictures() {
  const img= document.querySelectorAll("#alterAlbum > img"); //alterAlbumのimgのNodeListを獲得
   img.forEach(picture => {
    picture.addEventListener("click", () => {
      showPicture(picture)
    })
   })
}
window.addEventListener("load", () => linePictures());

//写真を表示する関数
function showPicture(img_pic){
  const table = document.getElementById('picTable');
  let picture = "static/pic/" + img_pic.src.split('/').pop() //画像のパスの指定
  let img = document.createElement('img')
  img.src = picture
  img.classList.add('table') //img.tableのCSSを適用させる
  table.appendChild(img) //picTableに子ノードを追加
  table.style.display = 'block' //picTableを表示

//表示した画像がクリックされたら閉じるようにする
  img.addEventListener('click', () => {
    table.style.display = 'none'
    table.removeChild(table.firstChild)
    img.classList.remove('table')
  })
}

画像をクリックするとこんな感じになります。
表示された画像をクリックすると閉じます。

f:id:CNaan:20181224031915p:plain
クリックして画像を表示した画像

表示する画像を切り替える

ここからが本題です。
折角なので、クリックした後、矢印キーを使って画像を切り替えたいですよね?
……ということで、右矢印で次の画像へ、左矢印で前の画像へ切り替わるようにします。
ついでに、それっぽいのでEscキーを押すとpicTableを非表示にします。

関数showPicture

function showPicture(img_pic){
  const table = document.getElementById('picTable');
  let picture = img_pic.src.substr(img_pic.src.indexOf("static", -1));
/*--追加--*/
//これを追加しないと移動したときに元の画像が表示されたままになる 
  while (table.firstChild) table.removeChild(table.firstChild);
/*--------*/
  let img = document.createElement('img')
  img.src = picture
  img.classList.add('table')
  table.appendChild(img)
  table.style.display = 'block'

/*--キー操作--*/
  document.onkeydown = function(e) {
    if (e) event = e;
    if (event) {
      if (event.keyCode == 27) { //Escキー
        table.style.display = 'none'
        table.removeChild(table.firstChild)
        img.classList.remove('table')
      }else if (event.keyCode == 39) { //右矢印キー
        showPicture(next(img_pic)) //次の画像を表示
      }else if(event.keyCode == 37) { //左矢印キー
        showPicture(previous(img_pic)) //前の画像を表示
      };
    };
  };
/*--------*/

  img.addEventListener('click', () => {
    table.style.display = 'none'
    table.removeChild(table.firstChild)
    img.classList.remove('table')
  })
}

関数next

 // 兄弟ノードの次のノードを返す、ない場合は先頭のノードを返す
function next(node) {
  const album = document.getElementById('alterAlbum');
  if(node.nextElementSibling) return node.nextElementSibling
  return album.firstElementChild
}
関数previous
 // 兄弟ノードの前のノードを返す、ない場合は末尾のノードを返す
function previous(node) {
  const album = document.getElementById('alterAlbum');
  if(node.previousElementSibling) return node.previousElementSibling
  return album.lastElementChild
}

できました。
関数nextとpreviousにはalterAlbumの子ノード(img)を渡しているので、
alterAlbumの中にあるimgを参照できる仕組みになっています。


nextSiblingとpreviousSibling

ところで、似たものとしてnextSiblingpreviousSiblingがあります。
違いとして、nextSiblingやpreviousSiblingは、空白のテキストノードを参照することがあります。
空白のテキストノードの参照を考慮したくない場合は、nextElementSiblingpreviousElementSiblingを使う方が良いのではないかと思います。

f:id:CNaan:20181224031620p:plain
空白のテキストノードを参照する例

おわり

以上で、今回の話は終わりです。
ノードの参照の話は面白いですね。
JavaScriptの面白い部分に触れられた気がします。