20200525のHTMLに関する記事は10件です。

初心者に捧げるヘッダーの作り方

初めに

初心者に捧げるハンバーガーメニューの作り方
初心者に捧げるシリーズ第二弾です。jQueryを使って、スクロールした時に変化のあるヘッダーを作ります!!
ソースコードをコメントで解説しながら記述していきます。

対象読者

・上部に固定したヘッダーを作りたい
・スクロールした時に変化するヘッダーを作りたい
・自分でオリジナルの見た目を作って応用したい人
・(ちなみに筆者の自己学習のためにも記事を書いているので間違っている箇所があれば是非とも教えて頂きたい)

目次

・完成イメージ
・HTMLの記述
・CSSの記述
・jQueryの記述
・まとめ

完成イメージ

ダウンロード.gif

HTMLの記述

<div class="container">
        <header>
            <ul>
                <!-- リンク先をつけるときはliの中に<a></a>を追加してその中に記述する -->
                <li>トップ</li>
                <li>ちくわの歴史</li>
                <li>世界のちくわ</li>
                <li>ちくわ豆知識</li>
                <li>よろちくわ</li>
            </ul>
        </header>

        <!-- スクロールするためのコンテンツ -->
        <div class="content">
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
          <p>aaa</p>
        </div>
    </div>

CSSの記述

/* HTMLに元からついているpaddingとmarginを消します */
        *{
            padding: 0;
            margin: 0;
        }
        header{
            /* 高さは好み */
            height: 64px;
            /* 上に固定するためにfixedとtop:0を書く */
            position: fixed;
            top: 0px;
            /* ボーダーは好み */
           border-bottom: 2px solid black;
            /* 幅は好み */
            width: 100%;
            /* 中央に揃える */
            text-align: center;
            /* メインのコンテンツより前面に表示 */
            z-index: 2;
        }

        ul {
            /* liについた点を消す */
            list-style:none;
            /* 上下のmarginの初期値は0なので:0 auto;じゃなくてもok */
            margin: auto;
            /* ヘッダーより前面に表示 */
            z-index: 3;
        }

        ul li {
            /* 横並びにする */
            display: inline-block;
            /* paddingは好み */
            padding: 26px 10px 20px 10px;
            /* 色は好み */
            color: black;   
        }
        /* スクロールを試すためなので自由 */
        .content {
            position: absolute;
            top: 100px;
        }

jQueryの記述

// $(function(){ HTMLの読み込みが完了した時に実行される
            $(function() {
                // windowがスクロールされた時
                $(window).scroll(function() {
                    // もし画面表示のtopが0より大きくなったら
                    if ($(this).scrollTop() > 0) {
                        // cssの記述は自由
                        $('header').css( 'background-color', 'black');
                        $('li').css( 'color', 'white');
                        $('header, li').css('transition', 'all 0.5s');
                    // 0より大きくなってない時
                    } else {
                        // cssは自由
                        $('header').css( 'background-color', 'white')
                        $('li').css( 'color', 'black');
                    }
                });
            });

まとめ

前回の初心者に捧げるハンバーガーメニューの作り方と合わせることもできるので是非とも見てみてください。
この記事をみてくださったあなたの成長を応援します!!!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Day24 javascript メニュータブの切り替え実装

メニュータブの切り替え実装

htmlでmenu項目about, service, contactをlist表記

index.html
<div class="container">
  <ul class="menu">
    <li><a href="#" id="about" class="menu_item active">Rails</a></li>
    <li><a href="#" id="service" class="menu_item">Javascript</a></li>
    <li><a href="#" id="contact" class="menu_item">Ruby</a></li>
  </ul>
  <ul class="contents">
    <li class="content show">
      about about about about about about about about about
    </li>
    <li class="content">
      service service service service service service service
    </li>
    <li class="content">
      contact contact contact contact contact contact contact
    </li>
  </ul>
</div>
style.css
ul.menu li .active {
  background: #353d3e;
  color: #fff;
}

ul.contents li {
  list-style: none;
  font-size: 14px;
  padding: 7px 10px;
  line-height: 1.4;
  background: #353d3e;
  color: #fff;
  min-height: 150px;
  display: none;
}

ul.contents li.show {
  display: block;
}

activeクラスとshowクラスが付いているものが、cssでdisplay: block;で表示できるようにし、付いていないものはdisplay: none;で表示できないようにしています。
タブメニューをクリックした時に、対応するクラスにactiveクラスとshowクラスを追加することで実装することができます。

main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = document.getElementsByClassName("menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除("[0]は、最初の要素の意味")
    document.getElementsByClassName("active")[0].classList.remove("active");
    // クリックしたタブにactiveクラスを追加
    // ②`this.`の後に、classListを使用してactiveクラスを追加しよう
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    // ③`document.getElementsByClassName('show')[0].`の後に、showクラスを削除しよう
    document.getElementsByClassName('show')[0].classList.remove("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcoutentクラスにshowクラスを追加する
    // ④`document.getElementsByClassName("content")[index].`の後に、showクラスを追加しよう
    document.getElementsByClassName("content")[index].classList.add("show");
  }

  // タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
  tabsAry.forEach(function(value) {
    // ①`value.`の後に、イベントリスナーでクリックイベントが発生した時に、tabSwitch関数を呼び出す処理を書きましょう。
    value.addEventListener("click", tabSwitch);
  });
});

Array.prototype.slice.call(引数); :引数にとったオブジェクトを配列に変換してくれる。
forEach() :配列に対してよく使われる繰り返し処理です。
indexOf() :inexOf()は配列に対してだけ使い、DOMを引数にとって一致した要素番号を戻します。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Day24 javascript, jQuery メニュータブの切り替え実装

メニュータブの切り替え実装(javascriptの場合)

htmlでmenu項目about, service, contactをlist表記

index.html
<div class="container">
  <ul class="menu">
    <li><a href="#" id="about" class="menu_item active">Rails</a></li>
    <li><a href="#" id="service" class="menu_item">Javascript</a></li>
    <li><a href="#" id="contact" class="menu_item">Ruby</a></li>
  </ul>
  <ul class="contents">
    <li class="content show">
      about about about about about about about about about
    </li>
    <li class="content">
      service service service service service service service
    </li>
    <li class="content">
      contact contact contact contact contact contact contact
    </li>
  </ul>
</div>
style.css
ul.menu li .active {
  background: #353d3e;
  color: #fff;
}

ul.contents li {
  list-style: none;
  font-size: 14px;
  padding: 7px 10px;
  line-height: 1.4;
  background: #353d3e;
  color: #fff;
  min-height: 150px;
  display: none;
}

ul.contents li.show {
  display: block;
}

activeクラスとshowクラスが付いているものが、cssでdisplay: block;で表示できるようにし、付いていないものはdisplay: none;で表示できないようにしています。
タブメニューをクリックした時に、対応するクラスにactiveクラスとshowクラスを追加することで実装することができます。

main.js
window.addEventListener("load", function() {
  // タブのDOM要素を取得し、変数で定義
  let tabs = document.getElementsByClassName("menu_item");
  // tabsを配列に変換する
  tabsAry = Array.prototype.slice.call(tabs);

  // クラスの切り替えをtabSwitch関数で定義
  function tabSwitch() {
    // 全てのactiveクラスのうち、最初の要素を削除("[0]は、最初の要素の意味")
    document.getElementsByClassName("active")[0].classList.remove("active");
    // クリックしたタブにactiveクラスを追加
    // ②`this.`の後に、classListを使用してactiveクラスを追加しよう
    this.classList.add("active");

    // コンテンツの全てのshowクラスのうち、最初の要素を削除
    // ③`document.getElementsByClassName('show')[0].`の後に、showクラスを削除しよう
    document.getElementsByClassName('show')[0].classList.remove("show");

    // 何番目の要素がクリックされたかを、配列tabsから要素番号を取得
    const index = tabsAry.indexOf(this);

    // クリックしたcoutentクラスにshowクラスを追加する
    // ④`document.getElementsByClassName("content")[index].`の後に、showクラスを追加しよう
    document.getElementsByClassName("content")[index].classList.add("show");
  }

  // タブメニューの中でクリックイベントが発生した場所を探し、下で定義したtabSwitch関数を呼び出す
  tabsAry.forEach(function(value) {
    // ①`value.`の後に、イベントリスナーでクリックイベントが発生した時に、tabSwitch関数を呼び出す処理を書きましょう。
    value.addEventListener("click", tabSwitch);
  });
});

Array.prototype.slice.call(引数); :引数にとったオブジェクトを配列に変換してくれる。
forEach() :配列に対してよく使われる繰り返し処理です。
indexOf() :inexOf()は配列に対してだけ使い、DOMを引数にとって一致した要素番号を戻します。

image.png

メニュータブの切り替え実装(jQueryの場合)

1.クリックしたタブのクラス要素を削除

removeClass()でクラス要素を削除

main.js
function tabSwitch() {
   $(".active").removeClass("active");
~~  ~~
   $('.show').removeClass("show");

2.クリックしたタブにクラス要素を追加

addClass()でクラス要素追加

main.js
function tabSwitch() {
   $(".active").removeClass("active");
   $(this).addClass("active");
   $('.show').removeClass("show");

3.クリックされた要素番号を戻す

クリックされた要素が何番目か、集合したDOM要素から引数に指定したDOMと同じ要素番号を戻す

main.js
const index = tabs.index(this);

index()は配列に戻す必要がない

4.リファクタリング

contentクラスの全てのshowクラスを削除した後、クリックされたタブの要素番号と一致するcontentクラスの要素だけ、showクラスを追加する

main.js
  function tabSwitch() {
    $(".active").removeClass("active");
    $(this).addClass("active");
    const index = tabs.index(this);
    $(".content").removeClass("show").eq(index).addClass("show");
  }
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Day24 javascript #DOM #ノード #イベント

DOMとは

Document Object Model(ドキュメントオブジェクトモデル)
HTMLを解析し、データを作成する仕組み
image.png
HTMLは階層構造になっていることが特徴です。
DOMによって解析されたHTMLは、階層構造のあるデータとなります。これを、DOMツリーやドキュメントツリーと呼びます。
image.png
JavaScriptを使うと、DOMツリーを操作することができます。
HTMLの要素名や、「id、class」といった属性の情報を元にDOMツリーの一部を取得し、CSSを変更したり、要素を増やしたり、消したりできます。
するとそれがブラウザに反映され、描画も変わります。DOMツリーの一部のことを、ノードオブジェクトと呼びます。

image.png

ノードの取得

document.getElementById("id名"); :id名での取得
document.getElementsByClassName("class名"); :class名での取得
document.querySelector("セレクタ名"); :セレクタ名での取得

addEventListener

main.js
let btn = document.querySelector("button");
// ボタンをノードオブジェクトとして取得し、変数btnに代入する

function printHello() {
  console.log("Hello world");
}
// printHello関数を定義

btn.addEventListener("click", printHello);
// ボタンのノードオブジェクトであるbtnに対して、
// clickイベントとprintHello関数を紐付ける仕組みであるイベントリスナを追加する

↑これだとhtml内のbuttonタグにたどり着く前にmain.jsが読み込まれるため、
"button"のノードオブジェクトを取得できない。

main.js
function printHelloWithButton() {
  let btn = document.querySelector("button");

  function printHello() {
    console.log("Hello world");
  }
  // 関数内で定義された関数は、関数の中でしか呼び出せない性質があるだけで、
  // 通常の関数同様に呼び出せる

  btn.addEventListener("click", printHello);
}
// 一連の処理をまとめた関数を作る

window.addEventListener("load", printHelloWithButton);

window.addEventListenerを追加することで、pageがloadされたら、
関数printHelloWithButtonが実行される。

↓今回、わざわざconsole.logを関数定義したのは、
window.addEventListenerの第二引数に関数を置く必要があるため。
そこで、function()と無名関数を定義してあげるとこのようになる。

main.js
window.addEventListener("load", function() {
  let btn = document.querySelector("button");

  btn.addEventListener("click", function() {
    console.log("Hello world");
  });
});

innerHTML

innerHTMLを使用するとHTML要素の中身を書き換えることができる

main.js
window.addEventListener("load", function() {
  let btn = document.querySelector("button");
  btn.addEventListener("click", function() {
    console.log("Hello world");
  });
  // テキストの要素を取得し、変数で定義
  let btn2 = document.querySelector("#Button2");
  let changeText = document.querySelector("p");
  // ボタン2をクリックしたらテキストが置換される
  btn2.addEventListener("click", function() {
    changeText.innerHTML = '変更されました';
  });
});

classList.add

「クラス追加」ボタンが押されたら、cssの背景色が赤のredクラスが追加されるようにclassList.addを利用します。

main.js
// Button3を取得して、変数で定義
let btn3 = document.querySelector("#Button3");

// クラス追加を押したらredクラスが追加される
btn3.addEventListener("click", function() {
  changeText.classList.add("red");
});

classList.remove

main.js
// Button4を取得して、変数で定義
let btn4 = document.querySelector("#Button4");

// div要素を取得して、変数で定義
let obj = document.querySelector("div");

// クラス削除を押したらblueクラスが削除される
btn4.addEventListener("click", function() {
  obj.classList.remove("blue");
});
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

webサイトを作るにはまずは「見えない力線」に注目して作ろう

デザインというものは見え方は人によって様々で一概にこれがいいというものは特にありません。
しかし見やすいとか、理解しやすいサイトというのは誰もが同じように感じるものではないでしょうか?
そこで基本的なことですが、改めて意識しておくといいことでもあるのでここに書かせていただきます。
今回は「見えない力線」について紹介したいと思います。
この見えない力線に意識してサイトを作成していくと自ずとユーザにとって見やすい。理解しやすいサイトになるでしょう。

「見えない力線」とは

要はガイドのことなんですが、サイトに見えないガイドを引き、それに沿っても文字や画像を配置することによって関連付けされた情報はただ単に見栄えがよくなるだけでなく、各要素に対する秩序をもたらし、そのページにきたユーザに情報を素早く理解できるようなサイトを作ることができます。

名称未設定-2.jpg

ダブルチェック以上のチェックをする

ただこの「見えない力線」に気をつけていても色々と修正していくと自分が気づかないうちにずれてしまうことがあります。
結構自分では気づかないところは他の人は気づくものです。私自身も他の人が作成したものはすぐに気づくのですが自分の作成したものを気づくのはなかなか難しくずれていたまま先に進めてしまうこともあります。
最近新しくLPを1人でコーディングして作成したのですが至る所にズレなどがあり自分の甘さに痛感しました。
1人でやり切らずダブルチェックなどは必ずやるべき項目ですね

まとめ

お洒落なサイトというものは流行などもあっていずれは古臭いデザインとなり廃れてきますが(全てがそうとも言い切れないが)、ユーザが見やすい、理解しやすいサイトというのはずっと愛されるものではないかと思っています。
初めてサイトを作ってみようという人はまずはオシャレなものというより、ユーザが使いやすい見やすいサイトを作ってみてはどうでしょうか?

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

実在する Web ページの bad practice を集めたサイト HTMHell の紹介

フロントエンド初心者の私です。
最近は実際に運用されている Web ページの HTML を読んで勉強することがありますが、本当にこの実装を鵜呑みにして良いのか...?と迷う場面が多々あります。
そんなときに見つけたサイト HTMHell が自分のニーズにハマったので紹介します。

HTMHell

HTMHell

title の "Markup from Hell" が良い味出してます。

Screen Shot 2020-05-24 at 23.53.26.png

A collection of bad practices in HTML, copied from real websites.

とあるように、実在する Web ページから bad practice を抜き出してきて、どこが悪いのか、どう改善すれば良いのかを紹介しているサイトです。

取り上げられているコードを幾つか紹介したいと思います。
※日々更新されているため、最新の情報と異なる場合があります。

#8 anchor tag used as button

#8 anchor tag used as button

Bad
<a href="#" onclick="modal.open()">Login</a>

モーダルのオープンに <a> タグを使用している例です。
実際によく見かけるような気もしますが、 <a> タグは本来はナビゲーション用途として使われるべきで、この例のように同一ページ内でのアクションを目的とする場合は <button> タグが適しているそうです。

Good
<button type="button" onclick="modal.open()">Login</button>

#9 Cookie Consent from Hell

#9 Cookie Consent from Hell

Bad
<body>
  <header></header>
  <main></main>
  <footer></footer>

  <div class="cookie_consent modal">
    <p>We use cookies…</p>
    <div class="cookie_consent__close">
      <i class="fa fa-times"></i>
    </div>
    <div class="cookie_consent__ok">OK</div>
  </div>
</body>

クッキー利用に対して同意を求めるモーダルの実装です。
海外のサイトでは特に画面下部に固定表示されているのをよく見かけますね。

この例では <body> の最下部にモーダル部分の実装が書かれているため、キーボードユーザーは Tab キーを連打してこのモーダルにフォーカスを移動させる必要があります。
しかも <div> 要素はデフォルトでフォーカスが当たらないのでそもそもモーダルに辿り着けない可能性もあります。
また他にも <div> 要素をボタンに使うと Assistive technology によるセマンティクスの理解の妨げになったり、 EnterSpace キーでボタンを押せなかったりなど色々問題があるようです。

これらを改善したコードは以下のようになります。

Good
<body>
  <div class="cookie_consent modal">
    <p>We use cookies…</p>
    <button class="cookie_consent__ok">OK</button>
    <button class="cookie_consent__close">
      <span class="sr-only">Close notification</span>
      <i class="fa fa-times" aria-hidden="true"></i>
    </button>
  </div>

  <header></header>
  <main></main>
  <footer></footer>
</body>

終わりに

英語なので全て読むのは骨が折れますが、実在する HTML の断片を集めただけあって実践的でよく遭遇するような内容が多く含まれています。
フロントエンド初心者の方は是非一度目を通してみてください!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

[Spring Boot] ページング処理簡単レシピ

環境

言語 : java 1.8
フレームワーク : spring boot 2.2.4.RELEASE (spring-boot-starter)
テンプレートエンジン : thymeleaf 2.2.4.RELEASE (spring-boot-starter)
データベース : H2 1.4.200 (spring-boot-starter)
orm : data-jpa 2.2.4.RELEASE (spring-boot-starter)

ソースコード

Entity

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Board {

    @Id @GeneratedValue
    private String id;

    private String title;

    private String user; 

    // Getter・Setter省略

}

Pagination

public class Pagination {

    /** 1. ページことに表示する掲示物数 **/
    private int pageSize = 10;

    /** 2. ページングした、ブロック数 **/
    private int blockSize = 10;

    /** 3. 現在ページ **/
    private int page = 1;

    /** 4. 現在ブロック **/
    private int block = 1;

    /** 5. 総掲示物数 **/
    private int totalListCnt;

    /** 6. 総ページ数 **/
    private int totalPageCnt;

    /** 7. 総ブロック数 **/
    private int totalBlockCnt;

    /** 8. ブロックスタートページ **/
    private int startPage = 1;

    /** 9. ブロック最後ページ **/
    private int endPage = 1;

    /** 10. DB接近スタートインデックス **/
    private int startIndex = 0;

    /** 11. 以前ブロックの最後ページ **/
    private int prevBlock;

    /** 12. 次のブロックの最後ページ **/
    private int nextBlock;

    // Getter・Setter省略

    public Pagination(int totalListCnt, int page) {

        // 総掲示物数と現在ページはコントローラーからもらう。
        // 総掲示物数  - totalListCnt
        // 現在ページ  - page

        /** 3. 現在ページ **/
        setPage(page);

        /** 5. 総掲示物数 **/
        setTotalListCnt(totalListCnt);

        /** 6. 総ページ数 **/
        setTotalPageCnt((int) Math.ceil(totalListCnt * 1.0 / pageSize));

        /** 7. 総ブロック数 **/
        setTotalBlockCnt((int) Math.ceil(totalPageCnt * 1.0 / blockSize));

        /** 4. 現在ブロック **/
        setBlock((int) Math.ceil((page * 1.0)/blockSize)); 

        /** 8. ブロックスタートページ **/
        setStartPage((block - 1) * blockSize + 1);

        /** 9. ブロック最後ページ **/
        setEndPage(startPage + blockSize - 1);

        /* === ブラック最後ページについてバリエーション ===*/
        if(endPage > totalPageCnt){this.endPage = totalPageCnt;}

        /** 11. 以前ブロックの最後ページ **/
        setPrevBlock((block * blockSize) - blockSize);

        /* === 以前ブロックについてバリエーション === */
        if(prevBlock < 1) {this.prevBlock = 1;}

        /** 12. 次のブロックの最後ページ **/
        setNextBlock((block * blockSize) + 1);

        /* === 次のブロックについてバリエーション ===*/
        if(nextBlock > totalPageCnt) {nextBlock = totalPageCnt;}

        /** 10. DB接近スタートインデックス **/
        setStartIndex((page-1) * pageSize);

    }
}

Controller

@GetMapping("/")
public String home(Model model, @RequestParam(defaultValue = "1") int page) {

    // 総掲示物数 
    int totalListCnt = boardRepository.findAllCnt();

    // 総掲示物数と現在ページ
    Pagination pagination = new Pagination(totalListCnt, page);

    // DB接近スタートインデックス
    int startIndex = pagination.getStartIndex();

    // ページことに表示する掲示物最大数
    int pageSize = pagination.getPageSize();

    // 掲示物取得
    List<Board> boardList = boardRepository.findListPaging(startIndex, pageSize);

    // モデルオブジェクトにオブジェクト格納
    model.addAttribute("boardList", boardList);
    model.addAttribute("pagination", pagination);

    return "index";
}

Repository

@Repository
public class BoardRepository {

    @PersistenceContext
    private EntityManager em;

    public int findAllCnt() {
        return ((Number) em.createQuery("select count(*) from Board")
                    .getSingleResult()).intValue();
    }

    public List<Board> findListPaging(int startIndex, int pageSize) {
        return em.createQuery("select b from Board b", Board.class)
                    .setFirstResult(startIndex)
                    .setMaxResults(pageSize)
                    .getResultList();
    }
}

html

<!DOCTYPE html>
<html   lang="ja" 
        xmlns="http://www.w3.org/1999/xhtml" 
        xmlns:th="http://www.thymeleaf.org">

<head>
    <meta charset="UTF-8">
    <title>paging</title>
        <link rel="stylesheet" th:href="@{/css/bootstrap.min.css}">
    </head>
<body>

<table class="table table-striped">

  <thead class="thead-dark">
    <tr>
      <th scope="col" style="width: 10%">no</th>
      <th scope="col">title</th>
      <th scope="col" style="width: 15%">user</th>
    </tr>
  </thead>

  <tbody>
    <tr th:each="board : ${boardList}">
      <th scope="row" th:text="${boardStat.index + 1}">1</th>
      <td th:text="${board.title}"></td>
      <td th:text="${board.user}"></td>
    </tr>
  </tbody>

</table>

// ページング
<nav aria-label="Page navigation example ">
  <ul class="pagination">
  <li class="page-item">
      <a class="page-link" th:href="@{/?page=1}" aria-label="Previous">
        <span aria-hidden="true"><<</span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{/?page={page} (page = ${pagination.prevBlock})}" aria-label="Previous">
        <span aria-hidden="true"></span>
      </a>
    </li>
    <th:block  th:with="start = ${pagination.startPage}, end = ${pagination.endPage}">
        <li class="page-item" 
                 th:with="start = ${pagination.startPage}, end = ${pagination.endPage}"
                th:each="pageButton : ${#numbers.sequence(start, end)}">
                <a class="page-link" th:href="@{/?page={page} (page = ${pageButton})}" th:text=${pageButton}></a>
        </li>
    </th:block>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.nextBlock})}" aria-label="Next">
        <span aria-hidden="true"></span>
      </a>
    </li>
    <li class="page-item">
      <a class="page-link" th:href="@{?page={page} (page = ${pagination.totalPageCnt})}" aria-label="Previous">
        <span aria-hidden="true">>></span>
      </a>
    </li>
  </ul>
</nav>

</body>
</html>

備考

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

HTML CSS参考サイト一覧

TechFeed

HTML,CSSだけでなくテクノロジーに興味のある人には、面白いと思います。
https://techfeed.io/

HPCode,サルワカ,taneppa

基本的なこと(displayって何だっけ?)に詰まった時に参考にしています。とても丁寧に書かれているので、理解しやすいです。
https://haniwaman.com/
https://saruwakakun.com/
https://taneppa.net/display-inline-block/

Headers

ヘッダーを作るときに苦労することが多かったのですが、このサイトには様々な種類のヘッダーが集められていて助けられました。
https://headers.netlify.app/

Web Design Search

Web Design、制作に役立つ情報がまとめられています。時間に余裕のある時に見てみてください。
https://webdesign-s.com/

shibajuku

アクセシビリティについて詳しく書かれているので、HTML,CSSを勉強し始めた初期段階で確認しておきたい。
https://shibajuku.net/category/accessibility/

ネコメシ

ここの記事を読んで勉強記録をQiitaに投稿しようと思いました。
良かったらお昼休みに読んでみてください。
https://necomesi.jp/blog/tsmd/posts/278#more-278

番外編

マーケティングについてまとめられているのですが、面白い。
https://ferret-plus.com/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

初心者によるプログラミング学習ログ 325日目

100日チャレンジの325日目

twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
325日目は、

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

CSSだけで幻想的な光を放つニキシー管を作れるのか?

はじめに

htmlとCSSでニキシー管を作ってみました。(デモのページ
動的な数字の切り替えに対応しているので、いろいろな用途で使えると思います。

ニキシー管とは

ニキシー管は文字や数字を表示させる発光装置です。ガラス管の中にネオンガスが入っていて、電圧を印加するとワイヤーが幻想的なオレンジ色の光を放ちます。今は数字を表示するデバイスは6セグのLEDにとって代わられてしまっていますが、そのスチームパンクな外観に加えて、デジタルな制御なのに表示される数字がアナログという良さから愛好家が多いデバイスです。STEINS;GATEのダイバージェンスメーターに使われているのはあまりにも有名です。
NixieClock

なぜ作ろうと思ったのか

ニキシー管は今は量産されておらず、手に入れようと思うと基本的にはソ連製のデッドストックを探してきて買うしかありません。供給がほとんどないので、1個あたりの値段は結構高いです。例えばこれとかは6個で1万円します。

勢いあまって購入したはいいのですが、ちゃんと制御しようとすると、140Vの電圧を作る昇圧回路、印加するピンを切り替える回路、複数本使う場合にはダイナミック点灯するための同期回路、そしてなんといっても1本あたり10本のピンが出ているのでそれの配線などなどが必要になりとても大変です。そこまでの覚悟がなかったので、手動で数字を点灯するところまでやって投げることになってしまいました...

リアルでは挫折してしまったのですが、web上なら回路を作る必要もないし制御もjavascriptとかで簡単にできるし、いいことづくめなのではと思って作り始めました。

できたもの

nixie_sample.gif

いろいろと試行錯誤を繰り返した結果、こんな感じになりました。
もっといい感じのニキシー管を作り上げてくれる人が現れることを願って、これ以降は制作工程を1から書いていきます。

ニキシー管の作り方

フォントを選ぶ

ニキシー管の独特な数字を再現するために、適切なフォントを探す必要があります。
ニキシー管で表示される数字は、ワイヤーで作られているものなので、次のような特徴があります。

  • 一筆書きで数字が表されている
    • 4の上がくっついている
    • 1の下の線はない
  • 線が細い
  • 構成している線が少ない

これに加えて、管を使っているので少し縦長という特徴もありますが、フォントの縦横比は後で変えられるので、フォント選定の際は考えなくても良さそうです。

この辺の特徴を満たすいい感じのフォントをGoogleFontsで頑張って探しました。線の細さでフィルターすると数が減って探しやすかったです。最終的には下記のフォントを使うことにしました。

Quicksand
font.PNG

光るワイヤーを作る

上で選んだ文字を光らせます。
CSSでネオンっぽく文字を表示する方法は下記のcodepenを参考にさせてもらいました。
CSS animated neon sign

ニキシー管の光は、

  1. ワイヤー部分の黄色く光る部分
  2. その周囲の蛍光物質が放電してできたオレンジ色に光る部分
  3. 光で照らされて淡くオレンジ色に光る部分

の3つの光があるとして、それぞれCSSに書いていきました。
1はテキストのcolorで設定し、2、3はtext-shadowを使って設定しています。text-shadowの色が薄い部分は複数回かけることでいい感じの濃さにしています。

また、元のフォントよりも横長にしたかったので
CSSは以下の通りです。

@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&display=swap');

.nixie_tube {
  font-size: 200px;
  font-family: 'Quicksand', sans-serif;
  color: #fff256;
  text-shadow: 0 0 0.05em #d45424, 0 0 0.05em #d45424, 0 0 0.05em #d45524,  /* 導線の輪郭 */
               0 0 0.1em #d45424, 0 0 0.1em #d45424, 0 0 0.1em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0.05em 0.01em #000;
  position: relative;
}

.light_up_wire{
  position: absolute;
  top: 0.2em;
  left: -0.3em;
  top: -0.15em;
  transform: scale(0.8, 1.0);
}
  <span class="nixie_tube">
    <span class="light_up_wire">{{show_num}}</span>
  </span>

ここまでやると↓のような感じになります。
font_color.PNG

光っていないワイヤーを作る

ニキシー管の中を見ると、数字の形をしたワイヤーが中に入っていることがわかります。
このごちゃっとした感じもニキシー管の良さですよね。というわけで、光っていない部分の数字も作ります。

先ほど作ったCSSのcolorを暗くして、text-shadowを消します。
また、positionをabsoluteにすることで、何個表示しても同じ場所に重なるように表示されるようにします。

.nixie_wire {
  color: #34241c;
  text-shadow: none;
  position: absolute;
  transform   : scale(0.8, 1.0);
}

htmlの方は0~9まで全ての数字を書いてしまうと、あまりに重なりすぎてなんだかわからなくなってしまうので、特徴的な数字だけ間引いて入れています。フォントの線も細く出来たら良かったのですが...

  <span class="nixie_tube">
    <span class="nixie_wire">0</span>
    <span class="nixie_wire">2</span>
    <span class="nixie_wire">7</span>
    <span class="nixie_wire">8</span>
    <span class="light_up_wire">{{show_num}}</span>
  </span>

できあがったワイヤーは↓のような感じです。
wire.PNG

ガラス管を作る

ニキシー管は試験管みたいな容器の中に入っているところがかっこいいです。
なので、いい感じの管もCSSで作ります。

ニキシー管のガラスの部分と上下のキャップを、指定した範囲の背景にグラデーションを設定し、枠を丸めることで作っています。ガラスの方は基本透明ながらも、オレンジの光が当たって側面が光っているようにしています。キャップの方は金属っぽくなるように上の方から光が当たって白くなるような感じで斜めのグラデーションをかけています。

.nixie_glass {
  width : 0.55em;
  height : 1.2em;
  position: relative;
  top: 0.18em;
  opacity: 0.5;
  background: radial-gradient(transparent 60%, #aa3315);
  border-radius: 0.15em;
  display: inline-block;
}

.nixie_cap {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: -0.08em;
  left: -0.025em;
    background: linear-gradient( transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.08em 0.08em 0.02em 0.02em;
  display: inline-block;
}

.nixie_buttom {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: 0.98em;
  left: -0.025em;
    background: linear-gradient(to top, transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.02em 0.02em 0.08em 0.08em;
  display: inline-block;
}

ここまで設定すると以下のようになります。

nixie.PNG

切り替え時の残像を再現する

ここまでで一応数字を表示するところまでできるようになったのですが、まだニキシー管のかっこいいところを再現しきれているとは言えません。ニキシー管はただ数字を表示しているだけでも素敵なのですが、時計などで数字が切り替わるとき、前に表示していた数字がすぐには消えずに残像となって残り、その間に新しい数字が出てくるような挙動をします。
参考動画
このふわっと感をCSSで再現したい...と思ったのですが、CSSで切り替わりのタイミングを制御する方法がわからなかったので、諦めてjavascriptを使いました。

フェードインとフェードアウトのアニメーションはCSSのアニメーション機能を使いました。text-shdowとcolorを徐々に変化させるようにしています。これを表示させる数値が切り替わるたびに実行するようにします。
また、フェードインさせる数字とフェードアウトさせる数字の二種類を作り、それぞれ同時にアニメーションさせることで切り替わりを再現しています。

.fadein {
  animation: fadein linear 0.2s;
}

.fadeout {
  animation: fadeout linear 0.2s forwards;
}

@keyframes fadein {
  0% {
    color: #34241c;
    text-shadow: none;
  }
  100% {
    color: inherit;
    text-shadow: inherit;
  }
}

@keyframes fadeout {
  0% {
    color: inherit;
    text-shadow: inherit;
  }
  100% {
    color: #000;
    text-shadow: none;
  }
}

これでふわっと数字が切り替わる様子が再現できました!
↓ではわかりやすいように実際よりゆっくりと切り替えています。
nixie_sample2.gif

完成

Vueのコンポーネントとして一つのファイルにまとめました。
これで好きな数字をpropから渡してあげることで、時計やカウンターのような使い方をすることができます。
デモのページでは、このコンポーネントを使ったデモを表示しています。

<template>
  <span class="nixie_tube">
    <span class="nixie_wire">0</span>
    <span class="nixie_wire">2</span>
    <span class="nixie_wire">7</span>
    <span class="nixie_wire">8</span>
    <span v-bind:class="{ fadeout: on_change }" class="nixie_wire">{{previous_num}}</span>
    <span v-bind:class="{ fadein: on_change }" class="light_up_wire">{{show_num}}</span>
    <span class="nixie_glass"></span>
    <span class="nixie_cap"></span>
    <span class="nixie_buttom"></span>
  </span>
</template>

<script>
export default {
  props: {
    show_num: [String, Number]  // ニキシー管に表示する数字
  },
  data: () => {
    return{
      on_change: false,
      previous_num: 8
    }
  },
  methods:{
    change_num: function() {
      if(this.show_num == this.previous_num){
        return
      }
      this.on_change = true
      setTimeout(()=>{
        this.on_change = false
        this.previous_num = this.show_num
      }, 200)
    }
  },
  watch:{
    show_num: function() {
      this.change_num()
    }
  },
  mounted() {
    this.change_num()
  }
}
</script>

<style>
@import url('https://fonts.googleapis.com/css2?family=Quicksand:wght@300&display=swap');

.nixie_tube {
  font-size: 200px;
  font-family: 'Quicksand', sans-serif;
  color: #fff256;
  text-shadow: 0 0 0.05em #d45424, 0 0 0.05em #d45424, 0 0 0.05em #d45524,  /* 導線の輪郭 */
               0 0 0.1em #d45424, 0 0 0.1em #d45424, 0 0 0.1em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0 0.5em #d45424, 0 0 0.5em #d45424, 0 0 0.5em #d45524,  /* 導線の輪郭 */
               0 0.05em 0.01em #000;
  position: relative;
}

.light_up_wire{
  position: absolute;
  transform: scale(0.8, 1.0);
}

.nixie_wire {
  color: #34241c;
  text-shadow: none;
  position: absolute;
  transform   : scale(0.8, 1.0);
}

.nixie_glass {
  width : 0.55em;
  height : 1.2em;
  position: relative;
  top: 0.18em;
  opacity: 0.5;
  background: radial-gradient(transparent 60%, #aa3315);
  border-radius: 0.15em;
  display: inline-block;
}

.nixie_cap {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: -0.08em;
  left: -0.025em;
    background: linear-gradient( transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.08em 0.08em 0.02em 0.02em;
  display: inline-block;
}

.nixie_buttom {
  width : 0.6em;
  height : 0.2em;
  position: absolute;
  top: 0.98em;
  left: -0.025em;
    background: linear-gradient(to top, transparent 40%, #aa5d31 70%,#111 80%),
                    linear-gradient(to top left, #111 60%, #444 80%);
  border-radius: 0.02em 0.02em 0.08em 0.08em;
  display: inline-block;
}

.fadein {
  animation: fadein linear 0.2s;
}

.fadeout {
  animation: fadeout linear 0.2s forwards;
}

@keyframes fadein {
  0% {
    color: #34241c;
    text-shadow: none;
  }
  100% {
    color: inherit;
    text-shadow: inherit;
  }
}

@keyframes fadeout {
  0% {
    color: inherit;
    text-shadow: inherit;
  }
  100% {
    color: #000;
    text-shadow: none;
  }
}
</style>

おまけ1

ニキシー管で好きな数字を表示できるようにして何をするかって?
それはもちろん...
raihousya.PNG

おまけ2

propには数値だけではなく文字を指定することもできます。
試しにやってみたところ、フォント側からしたらガラス管の幅なんて知ったことではないので容赦なくはみ出してきました。
hello.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む