20200322のHTMLに関する記事は8件です。

Electronでカレンダーを作る⑤

前回まで

前回はカレンダーの表示月を切り替えたら画面も切り替わるようにした。
ただ、切り替える度に画面更新で数秒真っ白になるのでダサかった。

レスポンシブルな感じのUIにしたい。

HTMLを最小限にする

HTML内にカレンダーの構造をそのまま書いているのでこれをごっそり削って最小限の記述にする。
カレンダーの構造自体はJavaScriptで作っていく。

before

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>ElectronCalendar</title>
  <script text="text/javascript" src="./js/index.js"></script>
  <link rel="stylesheet" href="./css/index.css">
</head>
<body>
  <div class="calendar-wrapper">
    <div class="preMonthButton">
      <button type="button" id="preMonth"><先月</button>
    </div>
    <div class="nextMonthButton">
      <button type="button" id="nextMonth">来月></button>
    </div>
    <table id="table" class="calendar">
      <caption id="caption"></caption>
      <thead>
        <tr>
          <th></th>
          <th></th>
          <th></th>
          <th></th>
          <th></th>
          <th></th>
          <th></th>
        </tr>
      </thead>
      <tbody>
        <tr id="row1">
          <td id="cell1"></td>
          <td id="cell2"></td>
          <td id="cell3"></td>
          <td id="cell4"></td>
          <td id="cell5"></td>
          <td id="cell6"></td>
          <td id="cell7"></td>
        </tr>
        <tr id="row2">
          <td id="cell8"></td>
          <td id="cell9"></td>
          ...略
          <td id="cell34"></td>
          <td id="cell35"></td>
        </tr>
        <tr id="row6">
          <td id="cell36"></td>
          <td id="cell37"></td>
          <td id="cell38"></td>
          <td id="cell39"></td>
          <td id="cell40"></td>
          <td id="cell41"></td>
          <td id="cell42"></td>
        </tr>
      </tbody>
    </table>
  </div>
</body>
</html>

after

index.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>ElectronCalendar</title>
  <script text="text/javascript" src="./js/index.js"></script>
  <link rel="stylesheet" href="./css/index.css">
</head>
<body>
  <div class="calendar-wrapper">
    <div class="preMonthButton">
      <button type="button" id="preMonth"><先月</button>
    </div>
    <div class="nextMonthButton">
      <button type="button" id="nextMonth">来月></button>
    </div>
    <div id="calendars"> 
    </div>
  </div>
</body>
</html>

<div id="calendars">ってなんでこのIDにしたか謎。

index.jsを改修する

index.js
'use strict';

//momentモジュール
const moment = require("moment");

// カレンダーをキャッシュしておくMap
let calendarMap = new Map();

// 画面に表示されている月
let currentDispMonth;

window.onload = function() {

    //URL文字列から初期表示の月を取得
    const month = parseURLParam(location.search).month;

    //直近3か月のカレンダー作成
    createLatestCalendar(month);
    //カレンダーの親要素取得
    const root = document.getElementById('calendars');
    //子要素としてパネルを追加
    const callendar = calendarMap.get(month);
    root.appendChild(callendar.getPanel());
    //カレンダー表示
    display(callendar.createContents);

    //先月ボタン押下時
    document.getElementById('preMonth').onclick = function() {
        const localMoment = moment(currentDispMonth);
        const preMonth = localMoment.add(-1,'month').format("YYYY-MM"); 
        const beforPanel = calendarMap.get(currentDispMonth).getPanel();
        const afterCallendar = calendarMap.get(preMonth);
        const afterPanel = afterCallendar.getPanel();
        changePanel(beforPanel, afterPanel, afterCallendar.createContents);
        createLatestCalendar(preMonth);
    };

    //来月ボタン押下時
    document.getElementById('nextMonth').onclick = function() {
        const localMoment = moment(currentDispMonth);
        const nextMonth = localMoment.add(1,'month').format("YYYY-MM"); 
        const beforPanel = calendarMap.get(currentDispMonth).getPanel();
        const afterCallendar = calendarMap.get(nextMonth);
        const afterPanel = afterCallendar.getPanel();
        changePanel(beforPanel, afterPanel, afterCallendar.createContents);
        createLatestCalendar(nextMonth);
    };
};

/**
 * URLパラメータを分解してkey:valueの形式で返す。
 * @param URL 
 */
function parseURLParam(URL) {
    // URLパラメータを"&"で分離する
    const params = URL.substr(1).split('&');

    var paramsArray = [];
    var keyAndValue = null;

    for(var i = 0 ; i < params.length ; i++) {
        // "&"で分離したパラメータを"="で再分離
        keyAndValue = params[i].split("=");

        // パラメータを連想配列でセット
        paramsArray[keyAndValue[0]] = keyAndValue[1];
    }

    // 連想配列パラメータを返す
    return paramsArray;
}

/**
 * パネルを切り替える
 * @param  beforPanel
 * @param  afterPanel
 * @param  callback パネル切り替え後に実行される関数
 */
function changePanel(beforPanel, afterPanel, callback) {
    //カレンダーの親要素取得
    const root = document.getElementById('calendars');
    //afterPanelでbeforPanelを置き換え
    root.replaceChild(afterPanel, beforPanel);
    display(callback);
}

/**
 * カレンダーを表示する。
 * @param callback 実行される関数
 */
function display(callback) {
    callback();
}

/**
 * 指定した月の直近(当月、先月、来月)のCallendarオブジェクトを作成する。
 */
function createLatestCalendar(month) {
    //当月分
    if(!calendarMap.get(month)) {
        const callendar = new Callendar(month);
        callendar.createPanel();
        calendarMap.set(month, callendar);
    }

    const localMoment = moment(month);

    //先月分
    const preMonth = localMoment.add(-1,'month').format("YYYY-MM");
    if(!calendarMap.get(preMonth)) {
        const preCallendar = new Callendar(preMonth);
        preCallendar.createPanel();
        calendarMap.set(preMonth, preCallendar);
    }

    //今月分
    const nextMonth = localMoment.add(2,'month').format("YYYY-MM");
    if(!calendarMap.get(nextMonth)) {
        const nextCallendar = new Callendar(nextMonth);
        nextCallendar.createPanel();
        calendarMap.set(nextMonth, nextCallendar);
    }

}

/**
 * カレンダークラス
 * @param month(YYYY-MM) 
 */
const Callendar = function(month) {

    //コンストラクタ
    this._month = month;
    this._moment = moment(this._month);
    this._panel = {};

    //コールバック関数で利用するのでthisを固定しておく
    const _this = this;

    /**
     * カレンダーのパネルを作成する。
     */
    this.createPanel = function() {
        // カレンダーのパネルを作成
        const panel = document.createElement('div');
        panel.id = 'panel';

        // 子要素にテーブルを追加
        const table = panel.appendChild(document.createElement('table'));
        table.id = 'table';
        table.classList.add('calendar');

        // テーブルにキャプション追加
        const caption = table.appendChild(document.createElement('caption'));
        caption.id = 'caption';

        // ヘッダー追加
        const header = table.appendChild(document.createElement('thead'));

        // ヘッダーのカラムを作成する。
        const headerRow = header.appendChild(document.createElement('tr'));
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';
        headerRow.appendChild(document.createElement('th')).innerText = '';

        // body作成
        let cellNum = 1;
        for (let i = 1; i < 7; i++) {
            const row = table.appendChild(document.createElement('tr'));
            row.id = 'row' + i;
            for(let j = 1; j < 8; j++) {
                const cell = row.appendChild(document.createElement('td'));
                cell.id = 'cell' + cellNum;
                cellNum++;
            }
        }
        this._panel = panel;
    };

    /**
     * カレンダーの内容を作成する。
     */
    this.createContents = function() {
        const __moment = _this._moment;

        //captionを表示
        document.getElementById('caption').innerText = __moment.format("YYYY年MM月");

        //当月の日数を取得
        const daysOfMonth = __moment.daysInMonth();

        //月初の曜日を取得(index.htmlと合わせるために+1する)
        const firstDayOfManth = __moment.startOf('month').day() + 1;

        //カレンダーの各セルに日付を表示させる
        let cellIndex = 0;
        for(let i = 1; i < daysOfMonth + 1; i++) {
            if(i === 1) {
                cellIndex += firstDayOfManth;
            } else {
                cellIndex++;
            }
            document.getElementById("cell" + cellIndex).innerText = i;
        }

        //6行目の第1セルが空白なら6行目自体を非表示にする。
        if(document.getElementById("cell36").innerText === "") {
            document.getElementById('row6').style.display = "none";
        }

        currentDispMonth = _this._month;
    };

    /**
     * パネルを取得する。
     */
    this.getPanel = function() {
        return this._panel;
    };

    /**
     * 月を取得する。
     */
    this.getMonth = function() {
        return this._month;
    };
};

修正・追加点

①カレンダーの構造を動的に作るようにする。

Calendarオブジェクトでカレンダーの構造(HTML要素)を作成して持っておくようにする。
Calendarオブジェクトは月ごとに一つ作られる。

display関数にCalendarクラスのcreateContentsメソッドをコールバック関数として渡すことで画面にカレンダーの内容が表示される。

index.js
/**
 * カレンダーを表示する。
 * @param callback 実行される関数
 */
function display(callback) {
    callback();
}

※コールバック関数をそのまま実行しているだけなのでコールバックという名前は不適なので修正したい。

②ボタンを押したら画面の更新無しでカレンダーを切り替えるようにする。

カレンダーの表示の切り替えは、changePanel関数でHTML要素を置き換える。

index.js
/**
 * パネルを切り替える
 * @param  beforPanel
 * @param  afterPanel
 * @param  callback パネル切り替え後に実行される関数
 */
function changePanel(beforPanel, afterPanel, callback) {
    //カレンダーの親要素取得
    const root = document.getElementById('calendars');
    //afterPanelでbeforPanelを置き換え
    root.replaceChild(afterPanel, beforPanel); ※
    display(callback);
}

※replaceChildメソッドの引数はreplaceChild(置き換えたいHTML要素(Node), 置き換え対象のHTML要素)となるので注意。

③カレンダーの切り替え毎に前後一ヶ月のCalendarオブジェクトを作っておくようにする。

初期表示では今月・先月・来月分のCalendarオブジェクトを作成しておき、今月分のカレンダーの表示させる。
以後、ボタン押下毎にCalendarオブジェクトが作られていく。

index.js
/**
 * 指定した月の直近(当月、先月、来月)のCallendarオブジェクトを作成する。
 * 対象月のCalendarオブジェクトがキャッシュされているならそれを使い回す。
 */
function createLatestCalendar(month) {
    //当月分
    if(!calendarMap.get(month)) {
        const callendar = new Callendar(month);
        callendar.createPanel();
        calendarMap.set(month, callendar);
    }

    const localMoment = moment(month);

    //先月分
    const preMonth = localMoment.add(-1,'month').format("YYYY-MM");
    if(!calendarMap.get(preMonth)) {
        const preCallendar = new Callendar(preMonth);
        preCallendar.createPanel();
        calendarMap.set(preMonth, preCallendar);
    }

    //今月分
    const nextMonth = localMoment.add(2,'month').format("YYYY-MM");
    if(!calendarMap.get(nextMonth)) {
        const nextCallendar = new Callendar(nextMonth);
        nextCallendar.createPanel();
        calendarMap.set(nextMonth, nextCallendar);
    }

}

なんかごちゃっとしてる。

index.js
    //先月ボタン押下時
    document.getElementById('preMonth').onclick = function() {
        const localMoment = moment(currentDispMonth);
        const preMonth = localMoment.add(-1,'month').format("YYYY-MM"); 
        const beforPanel = calendarMap.get(currentDispMonth).getPanel();
        const afterCallendar = calendarMap.get(preMonth);
        const afterPanel = afterCallendar.getPanel();
        changePanel(beforPanel, afterPanel, afterCallendar.createContents);
        createLatestCalendar(preMonth);
    };

    //来月ボタン押下時
    document.getElementById('nextMonth').onclick = function() {
        const localMoment = moment(currentDispMonth);
        const nextMonth = localMoment.add(1,'month').format("YYYY-MM"); 
        const beforPanel = calendarMap.get(currentDispMonth).getPanel();
        const afterCallendar = calendarMap.get(nextMonth);
        const afterPanel = afterCallendar.getPanel();
        changePanel(beforPanel, afterPanel, afterCallendar.createContents);
        createLatestCalendar(nextMonth);
    };

こっちもごちゃっとしてる。

・キャッシュしたCalendarオブジェクトの取得方法

Calendarオブジェクトはグローバル変数のMapに保存されているので月をキーにして取り出せる。

index.js
// カレンダーをキャッシュしておくMap
let calendarMap = new Map();

動かす

$ electron .
初期表示

image.png

来月ボタン押下時

image.png

画面更新で真っ白になることが無くなった!!!(画像じゃ分からない)

TODO

・コードをもっとスッキリさせたい。
・Electronの機能をもっと使いたい。

あとがき

・Javaの型で縛るプログラミングに慣れているので動的型付け言語は自由だけど不自由。
・ロジックを書くのはやっぱり楽しい。プログラミングの醍醐味だと思う。
・Electronの記事なのにほぼHTML・JSの記事になってる・・・。今回に関してはipc通信の箇所すら削っちゃってる!!!

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

scratchblocksを使ってScratchのブロックをサイトに埋め込む

読む前に

この記事ではHTML5を使ったサイトでの作成を想定しています。
WordPressを使用する場合は、プラグインがあるのでそちらをご覧下さい。

そもそもscratchblocksとは

blob8108氏が開発している、scratchのブロックをサイト上に埋め込むことの出来る、javascriptのプログラムです。
ソースコードはGithubで公開されています。

下準備

headタグ内に以下の二行を入れます。

html
<script src="https://scratchblocks.github.io/js/scratchblocks-v3.4-min.js"></script> 
<script src="https://scratchblocks.github.io/js/translations-all-v3.4.js"></script>

一行目ではscratchblocksのプログラムを、二行目ではブロックの翻訳ファイルを読み込んでいます。
英語でのみ使う方は、二行目はなくても大丈夫です。

ブロックを描写してみる

bodyタグ内にブロック名を入力し、それを任意のタグで囲みます。(今回はclassにscratchblocksを指定したdiv)

html
<body>
<div class="scratchblocks">
    hide
</div>
</body>

そして、</body>の直前に、以下の一行を追記します。

<script>scratchblocks.renderMatching('.scratchblocks');</script>

引数には、CSSセレクタと同じ要領で、タグやclass、idを指定します。
この状態でファイルを開くと、以下の様に表示されます。

See the Pen scratchblocks sample1 by Poteto143(活動少なめScratcher) (@poteto143) on CodePen.

hideと入力した部分がScratchのブロックに置き換わりました。

言語を指定する

このままの状態だと英語のブロック名しか使えません。
日本語でもブロックを正しく変換できるようするためには、scratchblocks.renderMatching()の第二引数に、オブジェクトでlanguagesというオプションを指定します。

html
<script>scratchblocks.renderMatching('.scratchblock', {languages: ["en", "ja"], style: "scratch3" });</script>

言語の指定はリストを渡します。
また、複数の言語を同時に指定することも可能です。
en(英語)とja(日本語)を指定して、「hide」と「隠す」をそれぞれブロックに変換すると以下の様になります。

See the Pen scratchblocks sample2 by Poteto143(活動少なめScratcher) (@poteto143) on CodePen.

ブロックの見た目を最新の物にする

ここまでで描写されたブロックは全てバージョン2.0のScratchの物です。
先ほどのオブジェクトにstyleというオプションを追加することで、バージョン3.0仕様のブロックの描写にも対応できます。

See the Pen scratchblocks sample3 by Poteto143(活動少なめScratcher) (@poteto143) on CodePen.

3.0仕様のブロックを使う場合はscratch3と入力します。
2.0仕様のブロックを使う場合は省略するか、scratch2と入力します。

ブロックをインライン表示する

文と文の間にブロックを描写したい場合は、まず変換する部分をspanタグで囲みます。
次に、scratchblocks.renderMatching()の第一引数でspanタグを選択します。
最後に、第二引数のオブジェクトにinlineというオプションを追加します。

See the Pen scratchblocks sample4 by Poteto143(活動少なめScratcher) (@poteto143) on CodePen.

インライン表示にする場合はtrueを、しない場合は省略するかfalseを入力します。

ブロックの記述方法

ブロックの記述方法については、Japanese Scratch-Wikiに記事がありますので、そちらをご覧下さい。
ブロックプラグイン (3.0)

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

謎の余白の正体 CSS編

余白ができた原因

・要素が二重で反映されている
・親要素からはみ出ている
・ファイルがダブっている

ここら辺を確認すると解決できます

今回の謎の余白問題

私の場合、子要素に、親要素あてにfloat:left;を指定したことによって、、、

小要素が親要素から横幅が出たことによって生じた現象でした

また、この謎の余白は、、、検証ツールでは指定できません

ここが、また厄介なところです。。

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

HTMLページをHerokuにデプロイ

この記事について

(お金をかけずに)SPAやアプリをインターネットに公開したいのにgithub pagesはもう使用済みでどうしよう、と思っていたところ、Herokuをおすすめしてもらい、実際に無料で公開できたので、手順を記録しておきたく記事にしました。

今回はHTML、CSS、JSファイルで作ったSPA(Single Page Application)をHerokuにデプロイしました。とりあえずこの記事ではデプロイするところまでをまとめています。

デプロイ後のログの確認や、コードの追加修正を反映させる操作などについては、別途まとめたいと思っています。

1. まず、HTMLで作ったSPAをアプリにする

HTMLやCSS、JSファイルだけで構成されたディレクトリはherokuではアプリとして認識されないため、HTMLファイルがあるディレクトリと同じディレクトリにindex.phppackage.jsonを追加して、phpアプリに変身させます。

index.php
<?php include_once("index.html"); ?>
package.json
{}

HTMLとJavaScriptで作ったサイトがHerokuにデプロイできねええって時の対処法
↑の記事を参考にさせていただきました。

2. Herokuアカウントを作る

Herokuでアカウントを作成します。

3. デプロイしていく

Getting Started on Heroku with PHPに沿ってデプロイをしていきます。

3-1. まず必要な準備

上記リンクに飛び、「Get started with php」をクリックすると、↓の画面に遷移します。
スクリーンショット 2020-03-21 19.24.24.png

始める前に、必要なもの準備できてますか?!という確認です。
下記の3つの準備が整っていることを求められています。

  • Herokuアカウントがあること
  • PHPがローカルにインストールされていること
  • Composerがローカルにインストールされていること

phpはMacにはデフォルトで入っているのでインストール不要でした。
ComposerはHomebrewからインストールします。
$ brew install composer

3-2. herokuをインストール

必要な準備が終わったら「I'm ready to start」をクリックして次に進みます。
次はherokuをインストールします。
スクリーンショット 2020-03-21 19.25.03.png
インストールコマンドが載っているのでターミナルで実行。
$ brew install heroku/brew/heroku

3-3. herokuにログイン

herokuのインストールが完了したらログインしてみます。

$ heroku login
と打つと、
heroku: Press any key to open up the browser to login or q to exit:
と返されるので、Enterします。

すると↓のページがブラウザに出るのでログインします。
スクリーンショット 2020-03-21 19.39.38.png

ログインした後に遷移するページは、そのページにも書いてありますが閉じてしまっても大丈夫なようです。

3-4. ツールのバージョン確認

php、composer、gitのバージョンを確認します。

バージョンの確認というか、インストールできてるかの確認です。下記それぞれのコマンドを実行してバージョンが表示されればインストールできているとわかります。

$ php -v
$ composer -v
$ git --version

3-5. herokuにデプロイするリポジトリをcloneする

githubからherokuにデプロイしたいリポジトリをローカルにcloneします。

$ git clone <url>

このリポジトリにはHTMLとCSS,JSで作られたSPAに、「1. まず、HTMLで作ったSPAをアプリにする」の手順で追加したphpファイルとjsonファイルが含まれています。

3-6. アプリをherokuにデプロイ

ローカルにクローンしたリポジトリに移動し、下記のコマンドを順番に実行していきます。

(1) $ heroku create

Creating app... done,

の後に続くワードが今回作成されたアプリ名になります。

(2)$ git push heroku master
(3)$ heroku ps:scale web=1
(4)$ heroku open

これでデプロイ完了です! HTMLで作ったSPAが新しいタブに表示されるかと思います。
もし表示されなくても、Herokuのダッシュボードを見るとアプリ名が表示されているので、そこへ飛び、右上の「Open」をクリックするとSPAに遷移できます。

終わりに

作ったものをデプロイしてURLを渡すだけで色々な人に見てもらえるようになるのは嬉しいですね。

プログラミングの勉強をし始めた時にも感じましたが、努力が何かしら形になるとモチベーション上がります!!SPA作ったとか、フレームワークのインストールができたとか、そういうことでも嬉しかったです。

デプロイ作業は今回初めてで、まだまだわからない事ばかりで手探り状態ですが、とりあえずできたので、しっかり運用できるようにしていきたいと思います。

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

初めてのレスポンシブ

初めまして

初めての投稿でして、先輩方よろしゅーおねがいします。
昨今の様々なニュースと機械学習組み合わせるときっと楽しんだろうって思ってsakura vpsでサイト立ち上げて 勉強開始してます。サイト立ち上げの苦労はまた今度。
今回はhtmlにつきものなレスポンシブってやつを勉強しました。
いろんなサイトをみてパクってきて再利用しようと思ったんですが、どうも複雑で。一部削除してもなぜか動いたり、期待した動きしなかったり。やっぱり基礎理解しないとダメだなって思って書きました。
 

実行例

下記にあります。
色々と色付けてわかりやすくしてみました。PCブラウザで枠幅を600以下にすると変わります。
https://dirtycode7.com/sample_css_responseive.html

悩んでるところ

なぜかすべてを半角文字にするとダメになる。
別に支障ないんだけど、とっても気になる。

今後

PCとスマホ、どうやって切り替えるか。先人の情報を見て考えます。

html部分

sample_css_responseive.html
<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" type="text/css" href="responseive.css" >
    <meta charset="utf-8"> 
    <title>かなりシンプルなレスポンシブのサンプル</title>
</head>

<body>
    <div class="header">
        かなーりシンプルなレスポンシブのサンプル
        (テーブルと枠の組み合わせ)
    </div>

    <div class="main">

        <table class="main_table">

            <tr>
                <th>1234567890123
                </th>
                <td>ああああああああああああああああああああああああああああああああああああああ</td>
            </tr>

            <tr>
                <th>1234567890123
                </th>
                <td>ああああああああああああ</td>
            </tr>

            <tr>
                <th>いいいいいいいいいいいいいいいいいいい
                </th>
                <td>うううううううううううううううううううううううううううううう</td>
            </tr>

        </table>
    </div>

</body>
</html>

css部分

responseive.css
/*********************************************/
/* フォント */
@import url('https://fonts.googleapis.com/css?family=Sawarabi+Gothic&display=swap');

/*********************************************/
/* 基本 */
html {
    position: relative;
    font-size: 1em;
    font-family: 'Sawarabi Gothic', sans-serif;
    color: #aaa;
    background-color: #000;
}

/*********************************************/
/* ヘッダー */
.header {
    font-size: 2em;
    font-weight: bolder;
    text-align: center;
    padding: 1em;
    margin: 1em 1em 1em 1em;
    background-color: #222;
}

/*********************************************/
/* メイン */
.main {
    padding: 1em;
    margin: 1em 1em 1em 1em;
    background-color: #222;
}

/*********************************************/
/* テーブル */
.main_table  {
    padding: 1em;
    background-color: #444;
}

/* 左 */
.main_table th  {   
    width: 10em;
    text-align: left;
    background-color: #666;
}

/* 右 */
.main_table td  {   
    position: relative;
    padding: 0.5em;
    margin-bottom: 1em;
    border-radius: 16px;
    display: inline-block;
    font-size: 2em;
    color: #eee;
    background: #555;
}

/* 狭いとき */
@media only screen and (max-width:600px){
    .header {
        color: #e00;
    }
    .main_table {
    }

    .main_table th {
        display: block;
        color: #0e0;
    }

    .main_table td {
        display: block;
        color: #00e;
    }

    .main_table tr:first-child th {
    }
}

/* EOF */

github

下記にあります。
https://github.com/dirtycode7/sample_css_Responsive

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

ライブラリなしでTreeTableを実装する

始めに

開発現場によってはjQueryやAngular等のフレームワーク、ライブラリの使用が禁止されているところがあると聞いたことがあります。また、使用するフレームワークによって実装方法がバラついてしまうことがよくあると思います。
そういった状況下でも、問題なく開発が進められるように、フレームワーク、ライブラリを一切使用せずTreeTableを自作してみました。
作成したTreeTableはgitHubに公開しております。

動作ブラウザ

・Google Chrome
・Microsoft Edge
・IE11

この3つ以外のブラウザでは未検証です。注意ください。

ソースの準備

ソースはgitHubに公開しております。
構成としては至ってシンプルで下記の3つのみです。
動作確認は上記で記載したブラウザがあれば確認することができます。

・サンプル用のhtml
・jsファイル
・cssファイル

TreeTable使用方法

サンプルで確認

サンプルは2つTreeTableを用意しました。
1つは一番シンプルな作成方法、もう1つはカスタマイズした作成方法になります。
ここではシンプルに作成した場合のサンプルについて確認します。

<script type="text/Javascript">
    // test data1
    const sampleDatas1 = [
        { Prefectures: 'tokyo', Municipality: null, town: null, name: '東京都', value: 100 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: null, name: '千代田区', value: 200 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 1, name: 'A町', value: 300 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 2, name: 'B町', value: 400 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 3, name: 'C町', value: 500 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 4, name: 'D町', value: 600 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 5, name: 'E町', value: 700 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: null, name: '中央区', value: 800 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 1, name: 'F町', value: 1000 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 2, name: 'G町', value: 1100 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: null, name: '港区', value: 1200 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 1, name: 'H町', value: 1300 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 2, name: 'I町', value: 1400 },
    ];

    window.onload = function () {
        // 設定1
        var settingJson1 = {
            // 作成する列(左から順に作成)
            columns: ['name', 'value'],
            // 階層識別列(親要素 - 子要素 - 孫要素)
            levelColumns:['Prefectures', 'Municipality', 'town']
        };

        // インスタンス生成
        var treeTable1 = new treeTable(this.document.getElementById('sample-tree-table1'), settingJson1);

        // TreeTable作成
        treeTable1.create(sampleDatas1);
    }
</script>

<div>
    <table id="sample-tree-table1">
        <thead>
            <tr>
                <td>名称</td>
                <td></td>
            </tr>
        </thead>
    </table>
</div>

上記はシンプルに作成するパターンです。作成する際はシンプルですが、データがやや複雑に見えます。
サンプルで作成したデータは3階層のデータ構造となっています。
「東京都→区→町」
表にすると下図みたいな感じです。
またデータを渡す際は、表のようにソートされているデータにする必要があります。

都道府県
東京都 null null
東京都 千代田区 null
東京都 千代田区 A町
東京都 千代田区 B町
東京都 千代田区 C町
東京都 千代田区 D町
東京都 千代田区 E町
東京都 中央区 null
東京都 中央区 F町
東京都 中央区 G町
東京都 港区 null
東京都 港区 H町
東京都 港区 I町

設定値

必須 項目 設定値 初期値 説明
isOpen Boolean true 表示時、展開するかどうか。
isTreeStatusKeep Boolean false 画面遷移しても展開状態を維持するか。(※1)
headerLabels 配列 なし 設定した場合、ヘッダ要素を作成。
必須 columns 配列 なし 表示する列要素。表示順は要素の順番。
必須 levelColumns 配列 なし 階層を識別するための列要素。
levelPadding 数値 20 階層毎の間隔。インデント。
onDrawCellValue function(value,cell,rowData){} なし セルに値を書き込む際のコールバックを設定。(※2)
onDrawRow function (row,rowData){} なし 行を描画する際のコールバックを設定。
iconStyle Json 下記で説明 開閉アイコンの設定
iconStyle.openText 文字列 + ツリーを展開している際に表示するテキスト。
iconStyle.openCss 文字列 tree-icon-open ツリーを展開している際に適用するcss。
iconStyle.closeText 文字列 - ツリーを閉じている際に表示するテキスト。
iconStyle.closeCss 文字列 tree-icon-close ツリーを閉じている際に適用するcss。

上記設定値を用いたTreeTableの作成は公開しているソース内に含んでいます。

isTreeStatusKeep ※1

Web Storage APIを使用しています。
ブラウザや環境等により使えない場合は、設定値をtrueで渡しても必ずfalseになります。
私の環境はEdge、IE11が使えず、Chromeは使えました。

onDrawCellValue ※2

指定した列の値を書き込む際のコールバックを設定します。
引数に設定する値は下記の3つです。

引数 内容
value セルに書き込む値
cell tdのDOM要素
rowData 1レコード分のデータ

指定する列は「columns」と対応する必要があります。

    columns: ['text','value'],
    onDrawCellValue: {
        'text': function (value, cell, rowData) {
            // 任意の処理
        },
        'value': function (value, cell, rowData) {
            // 任意の処理
        },
    }

参考にしたサイト

Web Storage APIの使用

最後に

様々なフレームワーク、ライブラリがオープンソース化されることで、それに依存してしまうことがあると思います。
依存してしまった結果、これがないとできないといった事象が起きてしまうことが少なからずあり、個人の技量が問われるようになったと感じています。
オープンソースを活用して工数を減らすことはもちろん大事ですが、仕様を理解せず振り回されないよう、技術を磨いていきたいものです。

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

JavaScript ライブラリなしでTreeTableを実装する

始めに

開発現場によってはjQueryやAngular等のフレームワーク、ライブラリの使用が禁止されているところがあると聞いたことがあります。また、使用するフレームワークによって実装方法がバラついてしまうことがよくあると思います。
そういった状況下でも、問題なく開発が進められるように、フレームワーク、ライブラリを一切使用せずTreeTableを自作してみました。
作成したTreeTableはgitHubに公開しております。

動作ブラウザ

・Google Chrome
・Microsoft Edge
・IE11

この3つ以外のブラウザでは未検証です。注意ください。

ソースの準備

ソースはgitHubに公開しております。
構成としては至ってシンプルで下記の3つのみです。
動作確認は上記で記載したブラウザがあれば確認することができます。

・サンプル用のhtml
・jsファイル
・cssファイル

TreeTable使用方法

サンプルで確認

サンプルは2つTreeTableを用意しました。
1つは一番シンプルな作成方法、もう1つはカスタマイズした作成方法になります。
ここではシンプルに作成した場合のサンプルについて確認します。

<script type="text/Javascript">
    // test data1
    const sampleDatas1 = [
        { Prefectures: 'tokyo', Municipality: null, town: null, name: '東京都', value: 100 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: null, name: '千代田区', value: 200 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 1, name: 'A町', value: 300 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 2, name: 'B町', value: 400 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 3, name: 'C町', value: 500 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 4, name: 'D町', value: 600 },
        { Prefectures: 'tokyo', Municipality: 'chiyoda', town: 5, name: 'E町', value: 700 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: null, name: '中央区', value: 800 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 1, name: 'F町', value: 1000 },
        { Prefectures: 'tokyo', Municipality: 'chuou', town: 2, name: 'G町', value: 1100 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: null, name: '港区', value: 1200 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 1, name: 'H町', value: 1300 },
        { Prefectures: 'tokyo', Municipality: 'minato', town: 2, name: 'I町', value: 1400 },
    ];

    window.onload = function () {
        // 設定1
        var settingJson1 = {
            // 作成する列(左から順に作成)
            columns: ['name', 'value'],
            // 階層識別列(親要素 - 子要素 - 孫要素)
            levelColumns:['Prefectures', 'Municipality', 'town']
        };

        // インスタンス生成
        var treeTable1 = new treeTable(this.document.getElementById('sample-tree-table1'), settingJson1);

        // TreeTable作成
        treeTable1.create(sampleDatas1);
    }
</script>

<div>
    <table id="sample-tree-table1">
        <thead>
            <tr>
                <td>名称</td>
                <td></td>
            </tr>
        </thead>
    </table>
</div>

上記はシンプルに作成するパターンです。作成する際はシンプルですが、データがやや複雑に見えます。
サンプルで作成したデータは3階層のデータ構造となっています。
「東京都→区→町」
表にすると下図みたいな感じです。
またデータを渡す際は、表のようにソートされているデータにする必要があります。

都道府県
東京都 null null
東京都 千代田区 null
東京都 千代田区 A町
東京都 千代田区 B町
東京都 千代田区 C町
東京都 千代田区 D町
東京都 千代田区 E町
東京都 中央区 null
東京都 中央区 F町
東京都 中央区 G町
東京都 港区 null
東京都 港区 H町
東京都 港区 I町

設定値

必須 項目 設定値 初期値 説明
isOpen Boolean true 表示時、展開するかどうか。
isTreeStatusKeep Boolean false 画面遷移しても展開状態を維持するか。(※1)
headerLabels 配列 なし 設定した場合、ヘッダ要素を作成。
必須 columns 配列 なし 表示する列要素。表示順は要素の順番。
必須 levelColumns 配列 なし 階層を識別するための列要素。
levelPadding 数値 20 階層毎の間隔。インデント。
onDrawCellValue function(value,cell,rowData){} なし セルに値を書き込む際のコールバックを設定。(※2)
onDrawRow function (row,rowData){} なし 行を描画する際のコールバックを設定。
iconStyle Json 下記で説明 開閉アイコンの設定
iconStyle.openText 文字列 + ツリーを展開している際に表示するテキスト。
iconStyle.openCss 文字列 tree-icon-open ツリーを展開している際に適用するcss。
iconStyle.closeText 文字列 - ツリーを閉じている際に表示するテキスト。
iconStyle.closeCss 文字列 tree-icon-close ツリーを閉じている際に適用するcss。

上記設定値を用いたTreeTableの作成は公開しているソース内に含んでいます。

isTreeStatusKeep ※1

Web Storage APIを使用しています。
ブラウザや環境等により使えない場合は、設定値をtrueで渡しても必ずfalseになります。
私の環境はEdge、IE11が使えず、Chromeは使えました。

onDrawCellValue ※2

指定した列の値を書き込む際のコールバックを設定します。
引数に設定する値は下記の3つです。

引数 内容
value セルに書き込む値
cell tdのDOM要素
rowData 1レコード分のデータ

指定する列は「columns」と対応する必要があります。

    columns: ['text','value'],
    onDrawCellValue: {
        'text': function (value, cell, rowData) {
            // 任意の処理
        },
        'value': function (value, cell, rowData) {
            // 任意の処理
        },
    }

参考にしたサイト

Web Storage APIの使用

最後に

様々なフレームワーク、ライブラリがオープンソース化されることで、それに依存してしまうことがあると思います。
依存してしまった結果、これがないとできないといった事象が起きてしまうことが少なからずあり、個人の技量が問われるようになったと感じています。
オープンソースを活用して工数を減らすことはもちろん大事ですが、仕様を理解せず振り回されないよう、技術を磨いていきたいものです。

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

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

100日チャレンジの269日目

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

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