まだプログラマーですが何か?

プログラマーネタとアスリートネタ中心。たまに作成したウェブサービス関連の話も http://twitter.com/dotnsf

タグ:css

2021 年、あけましておめでとうございます。昨年は人の集まりを自粛する機会が多く、不便に感じることも多くありましたが、一方で「オンライン」や「オフライン」であることを強く意識しながら自分のエンジニアとしてのスキルアップを考える機会も多くありました。今年は考えたり勉強したりしたことを色々な形でアウトプットできるよう心がけていこうと思っています。本年もよろしくおねがいします。


さて、そんな新年最初のブログエントリですが、簡易的な HTML プレビューワーを作ってみました。自分はウェブ関連の技術を紹介する機会が公私で多くあるのですが、そこで本当に紹介したい内容を話す際に HTML や CSS, JavaScript といったフロントエンドの基礎技術は理解しているという前提で紹介することがほとんどです。でも実際にはこの前提を満たしていない人に紹介するケースもあります。

考えようによっては「これから色んなことを勉強していきたい」という強い意志を持った人であって、そういう人が自分の話に興味を持ってもらえることは光栄でもあるのですが、とはいえこれらの基礎技術を理解できていない人にフロントエンドのコードを見せても(しかもここは理解している前提なので、詳しい解説を加えるのは別の部分)おそらくちゃんとは理解できないと思っています。願わくば、そのまま基礎技術にも興味を持ち続けて、自分で勉強も続けていただくことで、いつか理解の点と点がつながる日がやってくることが期待できるとも言えます。が、やはり前提を理解している人向けの説明では本来の難易度以上に難しく聞こえてしまうのか、説明している私からも手応えを感じにくい結果となってしまうことがほとんどです。

で、そういった人向けに、ある程度はその場でも(必要以上のツールのインストールなどをしなくても)HTML などの最低限のフロントエンド基礎をオンラインで独習したり、ある程度理解している人であればライブデモのようなことができるようなものを作ってみました。必要なものはオンラインのネット環境とブラウザだけで、オンラインミーティングとの併用でハンズオンを行う場合の相性がいいと思っています。ただテキスト編集の要素もあるツールなので、UI はスマホやレスポンシブではなく PC からの利用を想定しています(タブレットであればまあなんとか・・かも)。


【サービスの URL】
このツールは GitHub Pages を使って公開しています。URL はこちらです:
https://dotnsf.github.io/jch/


PC のブラウザからアクセスすると下のような4つの列からなる画面が表示されます:
2021010501


画面内の4つの列は左から JavaScript, CSS(スタイルシート), HTML, そしてこれらをまとめたプレビューです。左3つが編集エリアになっていて、その編集内容に応じて右のプレビュー結果が変わるようになっています。JavaScript や CSS に外部ライブラリを追加することはできませんが、jQuery だけは初めから使えるようにロード済みです。

初期状態はそれぞれ以下のようになっています:
JavaScript
2021010502

jquery.js をロードしています。そして $(function(){ と }); の間がテキストエリアになっていて JavaScript を記述できるようになっています(初期状態では空です)。ロード済みなので jQuery のセレクタを使った記述も可能です。

変更を加えてから "JavaScript" ボタンをクリックすると、変更内容がプレビューに反映されます。


CSS
2021010503

<style> と </style> の間がテキストエリアになっていて、スタイルシートの指定を自由に記述することができるようになっています(初期状態では空です)。

変更を加えてから "HTML" ボタンをクリックすると、変更内容がプレビューに反映されます。


HTML
2021010504

ベースとなる HTML が記述されています。初期状態では <h2> の見出しが1つと、<div> に囲われたリスト(<ul>~</ul>)が1つ記述されています。

変更を加えてから "HTML" ボタンをクリックすると、変更内容がプレビューに反映されます。


プレビュー結果
2021010505

上述の JavaScript, CSS, HTML の内容をすべて反映した結果がここに表示されます。初期状態では JavaScript と CSS が実質的に空なので、HTML に記述された内容(<h2> の見出しと <ul> のリスト)がそのまま表示されています。

ではこの初期状態を改良すべく、少しずつ編集してみましょう。まずは HTML に以下の赤字の行を加えてみます:
<h2> 50音 </h2>
<div id="xxx">
<ul>
<li id="li1" class="li">あいうえお</li>
<li id="li2" class="li">かきくけこ</li>
<li id="li3" class="li">さしすせそ</li>
<li id="li4" class="li">たちつてと</li>
<li id="li5" class="li">なにぬねの</li>
<li id="li6" class="li">はひふへほ</li>
<li id="li7" class="li">まみむめも</li>
</ul>
</div>

そして上部の "HTML" と書かれたボタンをクリックします。すると追加した2行のぶんが反映されて、プレビュー結果は以下のように変わります:
2021010506


次は CSS を追加してみます。初期状態では空だった <style> と </style> の間のスタイルシート編集部分に以下を追加します:
h2{
  font-size: 50px;
  color: 'blue';
}
#li3{
  color: '#f0f';
}

見出しである <h2> タグのフォントサイズ(50px)と色(青)を指定し、id="li3" の要素(さしすせそ)のみ紫色になるよう指定しています。そして最後に上部の "CSS" ボタンをクリックして、指定通りのプレビュー結果になることを確認します(下図では HTML 列は表示していません):
2021010507


続けて JavaScript も編集してみましょう。こちらも空だったテキストエリア内に以下を追加します:
$('#xxx').css( 'background', 'yellow' );
$('#li4').css( 'color', 'green' );

リストをラップしている <div id="xxx"> タグの背景色(黃)を指定し、id="li4" の要素(たちつてと)のみ緑色になるよう指定しています。そして最後に上部の "JavaScript" ボタンをクリックして、指定通りのプレビュー結果になることを確認します(下図では CSS 列と HTML 列は表示していません):
2021010508


HTML, CSS, JavaScript すべての変更が反映されたプレビュー結果が表示されることを確認できました。もちろんこのまま HTML, CSS, JavaScript に(順番関係なく)変更を追加していく都度、プレビューを確認することができます:
2021010509


このサービスを使うことで、最小限の準備作業で HTML や CSS, JavaScript といったフロントエンドの基礎技術を実際に編集しては結果を確認しながら学んでいくことができるようになると思っています。


【ツールのソースコード】
このサービスは上述のように GitHub Pages を使って公開していますが、実はページそのものもフロントエンドだけで作られています(アイコン画像などは別ファイルですが、サーバーサイドのバックエンド技術は使っていません)。要するにこのサービスも1枚の HTML とその中に記述された CSS, JavaScript だけで実現しています。

このサービスのソースコードも公開しているので、興味ある方は参照ください:
https://github.com/dotnsf/jch


特に該当 HTML ページのソースコードはこちらから参照できます:
https://github.com/dotnsf/jch/blob/main/index.html

ウェブアプリケーションで QR コードを表示したくなることがあります。スマホを持っている人にその場でとあるURLにアクセスしてもらったり、文字列などの情報を送るのに便利な方法だと思っています。

そんな機能が必要な場合、自分は jquery.qrcode.js を使っています。ブラウザの JavaScript(jQuery)だけで文字列の QR コードを簡単に生成することができます。サンプルページを用意したので、こちらで試すとわかりやすいと思います:
https://dotnsf.github.io/jquery_qrcode/

2020110601


画面最上部のテキストフィールドに URL や文字列を入力し、青いボタンを押すと入力した文字列から QR コードを生成して表示する、というものです。この部分は以下のようなコードで実現しています:
  :

<script src="./qrcode.min.js"></script>

  :

<div class="container">
  <input type="text" id="text" value=""/><button class="btn btn-primary" onClick="generateQRCode();">QRコード</button>
</div>

<canvas id="qrcode"></canvas>

<script>
var text = '';


function generateQRCode(){
  $('#qrcoe').html( '' );
  text = $('#text').val();
  if( text ){
    QRCode.toCanvas( document.getElementById( 'qrcode' ), text, function( err ){
      if( err ){
        console.log( err );
      }
    });
  }
}

  :

まず QR コードを描画するための <canvas> タグ(id="qrcode")を用意します。そしてボタンがクリックされるとテキストフィールドの値を取り出し、<canvas> の id を指定して QRCode を描画する、というシンプルなものです。これだけで QR コード化が実現できます。

文字列や URL を読み取らせたい対象者がスマートフォンユーザーだけであればこれだけで最小限必要な機能は実現できます。問題は対象者の中に PC ユーザーがいて、PC ユーザーにもこの URL や文字列を提供したい場合です。PC からは QR コードを読み取れない人が多数だと思うので直接メッセンジャーやメールなどで文字列や URL を送ればいいのですが、では逆に QR コードで表示されている文字列を取り出す便利な方法はないでしょうか? 上記サンプルの場合は QR コード化する文字列をテキストフィールドに指定しているのでテキストフィールドから取り出せばいいのですが、そういうケースばかりではありません。 以下は「QR コード部分をクリックした時に、その文字列をクリックボードにコピーする」ためのコードを加えました。これによってクリップボードからペーストするだけでメッセンジャー等で送れるようになり、UI的にも楽だと思ったのでした。 加えて、QR コード部分をクリックした際に「クリック感」というか「押された感」が出るような、一瞬だけ影ができるような視覚効果を加えてみました。

最終的なコードはこのようになりました:
  :
<script src="./qrcode.min.js"></script>

<style type="text/css">
html, body{
  text-align: center;
  background-color: #fafafa;
  font-size: 20px;
  color: #333;
}
#qrcode:active{
  border: 1px solid #334c66;
  background-color: #69c;
  -webkit-box-shadow:inset 0px 0px 8px #334c66;
  -moz-box-shadow: inset 0px 0px 2px #3a6da0;
  box-shadow: 0px 0px 2px #3a6da0;
}
</style>
</head>
<body>

<div class="container">
  <input type="text" id="text" value=""/><button class="btn btn-primary" onClick="generateQRCode();">QRコード</button>
</div>

<canvas id="qrcode"></canvas>

<script>
var text = '';

$(function(){
  $('#qrcode').on( 'click', function( e ){
    copyClipboard( text );
  });
});

function generateQRCode(){
  $('#qrcoe').html( '' );
  text = $('#text').val();
  if( text ){
    QRCode.toCanvas( document.getElementById( 'qrcode' ), text, function( err ){
      if( err ){
        console.log( err );
      }
    });
  }
}

function copyClipboard( str ){
  //. 空 div と空 pre
  var tmp = document.createElement( 'div' );
  var pre = document.createElement( 'pre' );

  pre.style.webkitUserSelect = 'auto';
  pre.style.userSelect = 'auto';
  tmp.appendChild( pre ).textContent = str;

  //. 画面外へ
  var s = tmp.style;
  s.position = 'fixed';
  s.right = '200%';

  //. body に追加
  document.body.appendChild( tmp );
  document.getSelection().selectAllChildren( tmp );

  //. クリップボードにコピー
  var result = document.execCommand( "copy" );

  //. 要素削除
  document.body.removeChild( tmp );

  return result;
}
</script>

  :

QR コード化したテキストの内容を変数 text に保存しておき、id="qrcode" のキャンバスがクリックされた時に text の値をクリップボードにコピーする、という内容を加えています。また視覚効果としてクリックされた時だけ有効になる #qrcode:active 要素を定義し、影をつけるなど見た目に変化を加えています。


ウェブ画面でタイル状のブロックを縦方向にいくつか並べて表示する際に「1つ上のタイルにぶらさがる」感じを出したいことがままあります。

例えば並べるブロックの数が不定で、画面を一度表示した後からブロックを追加したくなったりする際に「1つ前のブロックの続きである」ことを明確にするため、1つ前のブロックにぶら下がる感じで表現したい、というケースです。更に具体的にはブロックチェーンのブロックを並べるようなケースなどで、こんな風に:
2020101300


この「ぶらさがってる」感じはブロックの左右に紐状(上図ではチェーン)の画像を表示することで目的に近いものが表現できます。これを CSS で実現するにはどうすればよいか、というのが今回のテーマです。なお今回はブロック部分を Bootstrap4 の card クラスを使って実現していますが、他の多くの環境でも同様に実現できる、はず。

まず紐となる画像を用意しました。今回用意したのは↓のチェーン状の画像で、サイズは 23x50 ピクセルです(画像の高さである 50 を前提に以下の CSS を記述します):
chain


また HTML 的には以下のようにしたいと思っています。今回は Bootstrap4 の card クラスが付与された <div> を複数並べるのですが、2つ目以降の <div> には mycard クラスも付与し、この mycard クラスの CSS で2つ目以降のブロックは1つ前のブロックにぶら下がっているように表現できることを目指します:
<div class="container" style="padding:80px 0;">
  <div class="card text-white bg-primary border-danger">
    <div class="card-body">
      <h4 class="card-title">Title #0</h4>
      <p class="card-text">Text for #0</p>
    </div>
  </div>

  <div class="card text-white bg-primary border-danger mycard">
    <div class="card-body">
      <h4 class="card-title">Title #1</h4>
      <p class="card-text">Text for #1</p>
    </div>
  </div>

  <div class="card text-white bg-primary border-danger mycard">
    <div class="card-body">
      <h4 class="card-title">Title #2</h4>
      <p class="card-text">Text for #2</p>
    </div>
  </div>


    :
</div>

で、この mycard クラスをどのように定義すればよいか? が今回の課題なのですが、こんな感じで実現してみました:
<style type="text/css">
.mycard{
  margin: 55px 0;
}
.mycard::before{
  content: url(./chain.png);
  margin: 0px;
  position: absolute;
  top: -52px;
  left: 10px;
}
.mycard::after{
  content: url(./chain.png);
  margin: 0px;
  position: absolute;
  top: -52px;
  right: 10px;
}
</style>

まず mycard クラスを付与するブロックは画像(./chain.png)の高さぶんだけの余幅を持って表示される必要があります(その余幅に画像が入ります)。今回、画像の高さが 50px なので、少し余裕をもたせて margin: 55px 0; というスタイルを指定しています(つまり実際には 55px ぶん下に表示されることになります)。

この空いた余幅に画像を含めたいのですが、今回は上述のように左端と右端の両方に画像を入れ、2箇所で引っかかっているようにぶら下げようとしています。そのため ::before 要素::after 要素両方を使って mycard クラスの左端と右端に画像を差し込みます。

まず ::before 要素ですが、content に目的の画像を含めます(content: url( './chain.png' );)。更に画像位置をブロックの左端に設定するため、position: absolute; top: -52px; left:10px; を指定しています。これによって 50px の高さをもつ画像を 52px ぶん上から表示させて空間の隙間に収まるようにしています(また左端ギリギリではなく、10px ぶんのマージンを取っています)。

::after 要素にも同様の設定を行いますが、こちらは右端に表示したいため right: 10px; と、ブロックの右側に 10px ぶんのマージンができるような位置を指定しています。これによってブロックの右側に画像を表示させるよう指定しています。

こうして出来上がったサンプルがこちらです:
https://dotnsf.github.io/hanging_css/

2020101301


一番上の #0 には mycard クラスがついていないので、パネルのブロックがそのまま表示されてます。#1 以下には全て mycard クラスを指定しているのでチェーンでぶらさがる効果が付与されています。

この方法ならいくつでも足せるし、JavaScript で後から動的に足すこともできるし、ブロックの要素に mycard クラスを1つ追加するだけで実現できるので簡単です。

上記ページのソースコードをまるごと参照していただき、chain.png を同じフォルダに置いてブラウザで開けばローカルでも見れると思います。


贅沢をいうと、現在はチェーンの画像サイズに合わせて CSS を書いて調整しているのですが、この部分をもう少しスマートにできたらいいなあ、と。


CSS だけで画像やブロック要素にぼかしを入れることができます。説明の前に、実際に試せるウェブページを作って公開したので、こちらを御覧ください:
https://dotnsf.github.io/blur_change/

アクセスすると以下のような画面が表示されます。これはある画像がかなり強めにぼかされて表示されています。この時点では「なんとなく人間?」くらいはわかるかもしれませんが、性別や表情、姿勢など、どのような画像が表示されているのか、まだわかりませんよね:
2020090701


画面下にスライダーバーがあり、これでぼかしのレベルを調整できます。最初はスライダーの一番右(最大ぼかし)に設定されていて、これを左に移動させることでぼかしを少なくすることができます:
2020090702


だんだん元の絵が見えてきます:
2020090703


スライダーを一番左まで移動させるとぼかしはゼロになり、元の絵が表示されます:
2020090704


なお元の絵はいらすとや様の「ブレザーとネクタイの女子学生のイラスト」を使わせていただきました:



このようなぼかしを実現するには、CSS で対象要素(今回の場合は <img> 要素)に以下のようなスタイルを指定します:
#img{
  -ms-filter: blur(100px);
  filter: blur(100px);
}

この blur 属性の指定でぼかしを実現しています。上例では blur(100px); という指定をしていますが、この 100 の部分の数字が大きいほど強いぼかしが入り、0 だとぼかしもゼロになって元の画像のままで表示されます。

上述のサンプルページでは、スライダーバーの動きに従ってスタイル指定を書き換えることで動的にぼかしのレベルが変わるような JavaScript を実装しています。具体的には jQuery を併用して以下のような HTML および JavaScript を実装しました:
<script>
$(function(){
  var t = $('#img')[0];
  t.style['-ms-filter'] = 'blur(100px)';
  t.style['filter'] = 'blur(100px)';
});

function changeBlur(){
  var b = $('#rng').val();
  var t = $('#img')[0];
  t.style['-ms-filter'] = 'blur(' + b + 'px)';
  t.style['filter'] = 'blur(' + b + 'px)';
}
</script>
</head>
<body>

<div class="container">
  <img id="img" src="https://1.bp.blogspot.com/-anFajTcy3CM/XxU0nhH2UyI/AAAAAAABaOA/Fovyifhdjvs8HRvHhNZrOWXEDcTN1_E5gCNcBGAsYHQ/s450/school_blazer_girl_necktie.png" width="80%"/>
  <br/>
  <input id="rng" type="range" value="100" min="0" max="100" step="5" oninput="changeBlur()" width="100%"/>
</div>

この例ではスライダーバーの値に合わせてぼかしのレベルを変えていますが、他に setTimeout を使ったり、クリックイベントハンドラを使うなどして少しずつぼかしを薄くする、といった使い方ができると思っています。


なお、JavaScript で動的にスタイルを書き換える手法については以前のブログエントリで解説しているので、こちらも参照ください:
JavaScript でスタイルを動的に書き換える






ウェブページ内の特定要素の見た目を JavaScript で動的に変更する方法を調べました。

一般的にウェブページ内の要素の見た目はスタイルシートで管理されます。例えばこのような感じ:
<head>
<style>
#container1{
  background-color: #fcc;
  height: 100px;
}
</style>
</head>
<body>

<div class="container" id="container1">
</div>

</body>

この時点では #container1 要素はピンク(#ffcccc)で塗りつぶされて表示されます:
2020082601


この部分の「背景色を動的に(JavaScript で)別の色に変えたい」場合、いくつかの方法が考えられます。一般的には背景色が指定された class を複数用意し、id ではなく class 指定で背景色を与えた上で、JavaScript で指定 class を切り替える(jQuery であれば removeClass + addClass)方法です。 ただこの方法は「事前に class を定義しておく」必要があります。切り替える瞬間の情報や属性値を参照して背景色を決めるとか、ランダム要素を使って背景色を決めるなど、事前に定義する内容が定まらないような場合では使えません。このような場合も含めて #container1 要素に定義された内容そのものを変更する(つまりスタイル定義内容そのものを動的に変更する)にはどのようにすればよいかを調べました。以下、jQuery を併用した場合で説明します。

例えば #container1 で定義されるエリアをクリックしたら背景色をランダムに変える、という場合であれば以下のような JavaScript で実現できます:
<script>
$(function(){
  $('#container1').on( 'click', function( e ){
    //. #container1 のスタイルシート定義を動的に変える。具体的には背景色を変更する
    var bgs = [ '#fcc', '#cfc', '#ccf', '#cff', '#fcf', '#ffc', '#ccc' ];
    var bg = bgs[Math.floor( Math.random() * bgs.length )];
    var t = $('#container1')[0];
    t.style['background-color'] = bg;
  });
});
</script>

スタイルシートの内容を変更したい要素をセレクターを指定して取り出し、その取得結果(配列)の最初の要素を取り出します。そして取り出した要素の style 属性がスタイルシートの内容なのでここを JavaScript で上書きするよう記述します(上例では t.style 内の 'background-color' 属性をランダムに上書きしています)。この方法であれば class を使うことなく、切り替えることもなく、スタイルシート定義を書き換えることで見た目の変更を実現できます。


実際に動くサンプルを用意しました:
https://dotnsf.github.io/dynamic-css/


上記 URL にアクセスすると以下のような画面が表示されます。画面上部に高さ 100px でピンク色の矩形が表示されています:
2020082602


この矩形部分をクリック(タップ)すると、7種類の中からランダムに選ばれた背景色に切り替わります:
2020082603


JavaScript でスタイルシートの内容を動的に書き換える方法のサンプルでした。class を事前に用意して指定し直す、というロジックが使えない場合でも実現可能な方法です。


このページのトップヘ