- 投稿日:2020-03-20T23:46:02+09:00
Electronでカレンダーを作る③
前回からの続き
前回はカレンダーの中身が動的に表示されるようにした。
表示する月を変えられるようにする
先月、来月に切り替えられるようにしたい。
先月と来月切り替えボタンを配置する。
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="cell33"></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>※カレンダーに6行目を追加している。
index.js'use strict'; const moment = require("moment"); window.onload = function() { createCallendar(moment()); //先月ボタン押下時 document.getElementById('preMonth').onclick = function() { createCallendar(moment(this.value)); } //来月ボタン押下時 document.getElementById('nextMonth').onclick = function() { createCallendar(moment(this.value)); } } /** * カレンダーを表示する。 * @param momentオブジェクト */ const createCallendar = function(localMoment) { //captionを表示 document.getElementById('caption').innerText = localMoment.format("YYYY年MM月"); //カレンダー初期化 clearCallendar(); //当月の日数を取得 const daysOfMonth = localMoment.daysInMonth(); //月初の曜日を取得(index.htmlと合わせるために+1する) const firstDayOfManth = localMoment.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.visibility = "hidden"; } //先月 document.getElementById('preMonth').value = localMoment.add(-1,'month').format("YYYY-MM"); //来月(先月のmomentオブジェクトとなっているので+2ヶ月) document.getElementById('nextMonth').value = localMoment.add(2,'month').format("YYYY-MM"); } /** * カレンダーを初期化する */ const clearCallendar = function() { //6行目を表示させておく if(document.getElementById('row6').style.visibility === "hidden") { document.getElementById('row6').style.visibility = "visible"; } for(let i = 1; i < 43; i++) { document.getElementById("cell" + i).innerText = ""; } }6行目は表示しない場合がある(表示しない場合の方が多い)ので、表示しない場合は隠す。
$ electron .とりあえず出来たけどボタンがダサい。
TODO
・月を切り替えた場合、同一html内の表示内容の切り替えとなっているため、いちいち初期化が必要で微妙。新しく画面を読み込むようにしたい。
・ボタンの見た目をナウい感じにしたい。あとがき
Electronの記事じゃ無く、HTMLとJSの記事みたいになってる・・・。
- 投稿日:2020-03-20T22:27:20+09:00
レスポンシブにしたのに、なぜかなっていない。そんな時の確認。
- 投稿日:2020-03-20T19:45:57+09:00
【ラジオボタンより簡単!】jQueryで表示・非表示を実装する方法
CSSでうまく行くと思っていたのに...
最初はラジオボタンにdisplay:noneで
コーディングを行っていのですがタブがズレるズレる...
下に行ったかと思えば次は上に行くし、ため息ばかり...CSSでは歯が立たず『jQuery』で実装を行うことに。
jQueryの方が簡単でした!
mypage.js$(function(){ $('.mypage--list--info').click(function () { $('.tab--list--info').show(); $('.info-go-list').show(); $('.tab--list--todo').hide(); $('.todo-go-list').hide(); }); $('.mypage--list--todo').click(function () { $('.tab--list--todo').show(); $('.todo-go-list').show(); $('.tab--list--info').hide(); $('.info-go-list').hide(); }); $('.goods-purchase--transaction').click(function () { $('.tab-list--transaction').show(); $('.transaction-go-list').show(); $('.tab-list--post').hide(); $('.post-go-list').hide(); }); $('.goods-purchase--post').click(function () { $('.tab-list--post').show(); $('.post-go-list').show(); $('.tab-list--transaction').hide(); $('.transaction-go-list').hide(); }); })うまく表示されました!
jQueryでのコーティングは単調な繰り返しが多く、簡単に記載することができました。
CSSの方で実装頑張ってた自分がバカだった...1週間無駄にした...
- 投稿日:2020-03-20T19:22:46+09:00
HTMLのtableをソートする方法
概要
Tableをソートするライブラリを書きました
- 外部ライブラリなどは不要
- シンプルなので素のHTMLやBootstrapなど特にフレームワーク縛りなく使える
- ソートのみが欲しかったので容量小さめ
ソースコードはこちらで公開しています
デモ
See the Pen sortable-table (with Bootstrap4) by Tom Misawa (@riversun) on CodePen.
使い方
TableのHTML
- sortable-tableクラスをもった要素以下にtable theadを置く
- th要素にdata-id属性でデータとひもづけるidを指定する
- ソートしたい列にはsortable属性をつける
以下のようになる。
ソートできるテーブルのHTML<div class="sortable-table"> <table id="my-table1"> <thead> <tr> <th data-id="id" data-header> <div>#</div> </th> <th data-id="name" sortable> name </th> <th data-id="price" sortable> price($) </th> <th data-id="weight" sortable> weight(carat) </th> </tr> </thead> </table> </div>テーブルにデータを入れる
- 以下のようにテーブルに表示させたいデータを行ごとに配列で指定する
- 要素名とさきほどHTMLで定義したdata-id属性が紐付く
const data = [ { id: 0, name: 'Diamond', weight: 1.0, price: 9000, }, { id: 1, name: 'Amethyst', weight: 3.0, price: 200, }, { id: 2, name: 'Emerald', weight: 2.5, price: 2500, }, { id: 3, name: 'Ruby', weight: 2.0, price: 2000, }, ]; const sortableTable = new SortableTable(); // table要素を指定する sortableTable.setTable(document.querySelector('#my-table1')); // テーブルに表示したいデータを指定する sortableTable.setData(data);
- setTableでソートできるようにしたいtable要素を指定する
- setDataでテーブルに表示したいデータを指定する
See the Pen sortable-table (with plain HTML) by Tom Misawa (@riversun) on CodePen.
ソートした結果を受け取り処理する
ソート結果はgetDataで取得できる。
setDataで元のデータを指定するが、ソートしても元のデータは変更しないようにしているので、ソート済のデータを取得したい場合はgetDataか後述のイベントコールバックから取得する。const sortedData=sortableTable.getData();ソートイベントを受け取る
テーブルのヘッダ部分がクリックされ、ソートが実行されるとソートイベントが発火する
以下のようにイベントリスナーを登録するとソートされたときイベントがコールバックされる。sortableTable.events() .on('sort', (event) => { console.log(`[SortableTable#onSort] event.colId=${event.colId} event.sortDir=${event.sortDir} event.data=\n${JSON.stringify(event.data)}`); });
- event.colIdには列のid(=data-id)が格納される
- event.sortDirにはソート方向(=asc or desc)
- event.dataにはソート済のデータが格納される
ソートをコードから実行する
ユーザーのクリックではなく、コードからソートを(強制的に)実行する方法は以下の通り
ソートをコードから実行するsortableTable.sort('price', 'asc');sortableTable.sort([id], [sortDir]);のように指定する。
sortDirにソート方向を指定する
- asc・・・昇順ソート。昇順とは 1,2,3,4 のようになる
- desc・・・降順ソート。降順とは4,3,2,1 のようになる
- toggle・・・現在のソート方向を反転する。ソートを初期状態に戻す
最初にデータを指定したときの状態に戻すときはresetData
初期状態に戻すsortableTable.resetData();ソートをコードから実行するデモ
https://riversun.github.io/sortable-table/index_with_bootstrap.html
まとめ
- 拙作のソートライブラリについてお読みいただきありがとうございました
- 何かのお役にたてますと幸いです
ソースコードについて
Githubで公開しています
npm/yarnから利用可能です
npm install @riversun/sortable-table
- CDNからも利用可能です
<script src="https://cdn.jsdelivr.net/npm/@riversun/sortable-table@1.0.0/lib/sortable-table.js"></script>
- 投稿日:2020-03-20T18:04:04+09:00
【初心者用】htmalでwebサイトを作るときにまず入れる必須タグ
htmlでサイトをつくる際にまず知るべき事
htmalでwebサイトを作るときに基本的には「head」 「body」 「footer」
の3つの部分によって構成されています。
このあたりに関してはprogaetを学んでいる方ならご存知かとは思います。しかし、いざ実際にページを作成してみるとheadには何を入れればいいのか?、
bodyはどんな風にみせればいいの?という疑問が生まれるのではないでしょうか。そこで今日は
head前に入れるものとhead内冒頭にまず最初に必ず入れるべきコードをお伝えします。
そもそもheadとは
headはユーザーが見ることはできないwebサイトの基盤となっている部分です。
レストランで言えば厨房のようなイメージです。
このhead=厨房内で文字コードを入力し料理するからこそユーザーに見えやすいコンテンツが提供されているとそんな風に捉えて頂ければ大丈夫です。
!DOCTYPE html
htmlにてwebページを作成する際にこの文章はhtmlで書きます!と宣言する必要があリます。
そこでこの!DOCTYPE htmlをいれることによってこのファイルhtmalで作成します!と宣言することができるんですね。
この!DOCTYPE htmlまずファイルを作成した一番はじめに入れるようにしましょう
html lang=“ja”
次にすべき事はこのhtmlコードは日本語で描きます!と宣言する作業です。
html lang=“ja”とありますが英語の場合はhtml lang=“en”となり、それぞれの言語に合わせて最初に入れておきましょう。
meta charset="utf-8"
コンピューター上には文字を理解するために必要な文字コードというものが存在します。
このmeta charset="utf-8"は文字コードの指定を表していますのmetaって何?という方、
metaタグについてはまたお伝えしますが、いまはこのmeta charset="utf-8"を入力するとパソコン側がutf-8をつかえばいいのね!と理解してくれると覚えてください。
プログラミングはただ丸暗記する必要はありませんが基盤となる概念は理解して置く必要があります。なぜこのコード、タグが必要なのか、そこだけ理解できれば他を暗記する必要はありません。
次回はその他にheadに入れるべきタグをご紹介します。
- 投稿日:2020-03-20T17:14:41+09:00
Electronでカレンダーを作る②
前回からの続き
前回は画面まで作成した。
カレンダーの内容を動的に表示する処理を作成していく。カレンダーの中身を作っていく
日付操作を楽にするためmomentモジュールを使う。
$ npm install -D momentindex.htmlを修正していく。
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ElectronCalendar</title> <script text="text/javascript" src="index.js"></script> <link rel="stylesheet" href="index.css"> </head> <body> <div class="calendar-wrapper"> <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> <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> ・・・略 <td id="cell27"></td> <td id="cell28"></td> </tr> <tr> <td id="cell29"></td> <td id="cell30"></td> <td id="cell31"></td> <td id="cell32"></td> <td id="cell33"></td> <td id="cell34"></td> <td id="cell35"></td> </tr> </tbody> </table> </div> </body> </html>7(曜日) × 5(週)の箱を作って各セルにIDを振っておく。
index.js'use strict'; const moment = require("moment"); window.onload = function() { //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; } }月初の曜日が分かればそれ以降の日付の曜日も一意に決まる。
カレンダーの特性上、1番上の行のセルには値が必ず一個は入るので、cellIndex += firstDayOfManth; で初日のセルの位置を設定して、後は日数分インクリメントしていく。月初はmomentモジュールのmoment().startOf('month')で楽に取得できる。
$ electron .祝日を赤字にするにはどっかでデータを持っておかないと無理そう。
TODO
年月を変更できるようにする。
あとがき
momentモジュールが便利。
- 投稿日:2020-03-20T17:06:16+09:00
Railsのtext_fieldにCSSをあてる方法
text_fieldにCSSってどうやってあてるんだっけ?
Railsアプリケーション作成時にフロント部分を作成していた時に
example.haml.html.item__name 商品名 .name--input = f.text_field :name, placeholder: "40文字まで"のようなhamlを作成していざscssを記述しようとしたときに
このtext_filed
にどうやってCSSをあてるのかが
ふと考えると分からなかったんです。同じような
number_field
やsubmit
などにも使えるやり方なので
是非覚えておいたほうがよいです。そもそも
text_field
で作られるHTMLは何なのか?上の
= f.text_field :name, placeholder: "40文字まで"
で作られるHTMLを
chromeの検証で確認してみると
<input placeholder="40文字まで" type="text" name="item[name]" id=item_name>
というものが作成されているのがわかるかと思います。text_fieldとかはRailsがviewを簡潔に記述するために用意してくれているヘルパーメソッドのため
簡単な記述で実際はこういうHTML文の作成もしてくれています。
この作成されたHTMLにあてるようにCSSを記述すればOKです。今回はSCSSを使い記述しました。
CSSをあててみよう
example.scss.item__name{ input[type="text"]{ width: 100%; height: 20px; font-size: 14px; } ::placeholder{ padding: 5px 5px; } }と指定して記述すると、
text_field
にCSSをあてることができます。
placholder
はCSSの擬似要素のため上記のような記述をする必要があります。ヘルバーメソッドを使って記述した場合にどのようなHTML文が作成されているのかを確認すると解決できる部分でしたね。
参考先
- 投稿日:2020-03-20T15:21:30+09:00
Electronでカレンダーを作る①
はじめに
皆さん、Electron触っていますでしょうか?
私は最近Electronの存在を知ったので、JavaScriptの勉強も兼ねてカレンダーアプリを作成してみようと思った次第です。とりあえずElectronプロジェクトを作る。
$ mkdir ElectronCalender $ cd ElectronCalender/ $ npm init -y $ npm install -D electron ※これでディレクトリ内にElectronを使う準備は整った。
※npm install -D ~ はpackage.jsonに書き込みらしい。画面を作ってアプリを立ち上げる。
package.jsonにエントリーポイント(main.js)を指定しとく。
package.json{ "name": "ElectronCalender", "version": "1.0.0", "description": "", "main": "main.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "devDependencies": { "electron": "^8.1.1" } }main.jsの中身
main.js'use strict'; // Electronのモジュール const electron = require("electron"); // アプリケーションをコントロールするモジュール const app = electron.app; // ウィンドウを作成するモジュール const BrowserWindow = electron.BrowserWindow; // メインウィンドウはGCされないようにグローバル宣言 let mainWindow; // 全てのウィンドウが閉じたら終了 app.on('window-all-closed', function() { if (process.platform != 'darwin') { app.quit(); } }); // Electronの初期化完了後に実行 app.on('ready', function() { // メイン画面の表示。ウィンドウの幅、高さを指定できる mainWindow = new BrowserWindow({width: 800, height: 600}); mainWindow.loadURL('file://' + __dirname + '/index.html'); // ウィンドウが閉じられたらアプリも終了 mainWindow.on('closed', function() { mainWindow = null; }); });mainWindow.loadURL('file://' + __dirname + '/index.html'); で表示する画面(htmlファイル)を読み込む。
html,cssはこんな感じ(ほぼ参考書の内容を丸写しした)
index.html<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>ElectronCalender</title> <link rel="stylesheet" href="index.css"> </head> <body> <div class="calendar-wrapper"> <table class="calendar"> <caption>2020年3月</caption> <thead> <tr> <th>日</th> <th>月</th> <th>火</th> <th>水</th> <th>木</th> <th>金</th> <th>土</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>2</td> <td>3</td> <td>4</td> <td>5</td> <td>6</td> <td>7</td> </tr> <tr> <td>8</td> <td>9</td> <td>10</td> <td>11</td> ・・・略 </tr> </tbody> </table> </div> </body> </html>index.css.calendar-wrapper { width: 750px; } .calendar { table-layout: fixed; width: 100%; border-collapse: collapse; border: 1px solid #ccddcc; vertical-align: middle; } .calendar th, .calendar td { padding: 10px 0; border: 1px solid #cdcdcd; text-align: center; vertical-align: top; font-size: 35px; } .calendar th { background-color: #dedede; } /* 日曜日1行目(見出し) */ .calendar th:first-child { background-color: #e05557; color: #ffffff; } /* 日曜日 */ .calendar td:first-child { color: #e05557; } /* 土曜日1行目(見出し) */ .calendar th:last-child { background-color: #207bcf; color: #ffffff; } /* 土曜日 */ .calendar td:last-child { color: #207bcf; } /* 祝日 */ .holiday { color: #e05557; } .calendar caption { margin: 0 0 10px 0; padding: 10px; font-size: 23px; border: 5px solid #dedede; border-radius: 30px; font-weight: bold; }アプリを起動する。
ディレクトリ直下で $ electron .良い感じな気がする。
TODO
日付、祝日をhtml内にベタ書きしているのでjsを使って動的にカレンダーの内容が作成されるようにしたい。
あとがき
作っていて気がついたけどカレンダーのスペルは「calender」じゃなくて「calendar」だった。
- 投稿日:2020-03-20T14:36:55+09:00
Railsでページ毎にsubmitの表記を変えたい
submitボタンの表記を変えたい
submitボタンを実装した場合、デフォルトの日本語表記は下記のような形で表示されます
(色付けなどはCSSで加工済み)'登録する' というのがデフォルトのsubmitボタンとなります。
例えば、作成しているアプリで商品出品するページでは当然、'出品する'という表記にしたいですよね。
しかし、商品を編集して更新する場合は、'出品する'だと違和感があるので
'更新する'と表記をしたいところです。しかし、出品も更新も同じフォームを流用しているので
分岐分けで記入する必要があるところになります。単純にsunmitボタンのテキストを変えたい場合なら
<input type="submit" value="出品する">
を使えば表記は変わるわけなのですが、上記のように分岐わけさせたいときにどうするべきか。
出品と更新のページを別々に作って、各々でテキスト表記を変えるという手段もできますが
メンテナンス上、ほぼ同じ記述のコードのファイルが増えてしまうのは避けたいところです。ja.ymlを利用して分岐させる
ja.ymlといえば、英語表記を日本語化させる指定を記述する場所なのですが
こちらでアクション毎にsubmitの表記を指定させることができます。まずはi18nの導入が必要なので参考先にあるページを参照に導入を済ませておいてください
[初学者]Railsのi18nによる日本語化対応
config/locales/ja.yml
に記述するja.ymlja: helpers: submit: create: "出品する" update: "更新する"このように記述をするとcreate下のsubmitは出品すると表示され
update下のsubmitは更新すると表示されるようになります。実際にcreate時はこのように変更がかかります。
update画面も同様に記述した通りの変更がかかります。これでhtmlをcreateとupdateで別々に表記を分ける必要がなくなるので
ファイルを増やすことなく分岐して表示させることができます。参考先
http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-submit
- 投稿日:2020-03-20T13:58:37+09:00
WebにおけるUIのキーボード操作まとめ
Webアプリケーションを実装する上でHTMLのキーボード操作について調べたメモです。
なぜ、キーボード操作が必要なのか
- 運動障害を持つ多くのユーザーはキーボードに依存している(これは健常者が怪我した場合も含まれる)
- 視覚障害のあるユーザーも通常、ナビゲーションにキーボードを使用する
- 一部のユーザーはキーボードを模倣した別のハードウェアを使う場合がある
- もしかしたら、マウスが壊れてキーボードしか使えない状況もありうる
- というか、普通にキーボードで操作できると便利じゃないですか?
WCAG 2.1の「ガイドライン2.1 操作可能」では以下のように記載されています。
すべての機能をキーボードから利用できるようにすること。
HTMLキーボード操作一覧
キーボード操作はアクセシビリティにおいて重要な部分です。キーボードを通してページを操作できるだけでなく、予測可能であることが重要です。これには一般的なキーボード操作の理解が必要になります。
次の表にはオンラインにおけるインタラクションの標準キーストロークおよび考慮すべき情報が記載されています。
インタラクション キーストローク Tab Enter SpaceBar 方向キー Esc 備考 要素移動 ・Tabによる前方移動
・Shift + Tabによる後方移動● ・フォーカスインジケータが表示されている必要がある
・移動の順序は論理的かつ直感的でなければいけないリンク Enter ● ボタン EnterもしくはSpaceBar ● ● role="button" の要素で両方のキーコマンドが有効であること チェックボックス SpaceBarによってチェックボックスのON/OFFを切り替える ● 複数のオプションを選択できる場合はチェックボックスを使用すること ラジオボタン ・方向キー(↑→↓←)でオプションを選択
・Tabで次のオプションに移動● ● 1つのオプションを選択できる場合はラジオボタンを使用すること ドロップダウンメニュー ・方向キー(↑↓)でオプション移動
・SpaceBarでメニューを展開● ● オートコンプリート ・入力時にフィルタリングを開始
・方向キー(↑↓)でオプションを移動
・Enterでオプションを選択● ● ダイアログ Escで閉じる ● ・モーダルダイアログはキーボードフォーカスを維持する必要がある
・モードレスダイアログはフォーカスを失うと閉じる
・ダイアログを閉じた場合、フォーカスは通常ダイアログを開いた要素に戻るスライダー ・方向キー(↑→↓←)で値の増減 ● ・ダブルスライダー(範囲選択)の場合はTab、Shift + Tabで両端を切り替える
・一部のスライダーではPageUp/PageDownにより増減できる
・Home/Endで開始/終了に移動する場合もあるメニューバー ・方向キー(↑↓)でオプションを移動
・方向キー(←→)でサブメニューの開閉
・Enterでオプションを展開もしくは選択● ● ・すべてのメニューにこれらのコントロールがあるわけではない。よりシンプルなメニューはTab/Enterに依存する タブパネル ・Tabでタブグループの中に入り、Tabでタブグループの外に移動する
・方向キー(↑→↓←)で前後のタブに移動● ● ・これは新しくロードせずにタブ移動できるタブ用
・タブが別のページへのリンクの場合はTab + Enterが適切
・方向キーを押すと、タブのコンテンツが自動で更新される(Enterキーは基本的に使わない)ツリーメニュー ・方向キー(↑↓)でオプションを移動
・方向キー(←→)でサブメニューの開閉もしくは1階層移動● スクロール ・方向キー(↑↓)で垂直にスクロール
・方向キー(←→)で水平スクロール
・SpaceBar / Shift + SpaceBarでページ単位でスクロール● ● 水平スクロールは最小限にする フォーム ・基本的にEnterでサブミット(備考参照) ● ・サブミットボタンがある場合はinput要素に限らず、Enterでサブミットできる
・Enterキーでサブミットしたくない場合はサブミットボタンをdisabledにする
・サブミットボタンが無い場合はinput要素の数が1つか複数かで挙動が異なる
・1つの場合はEnterキーでサブミットできる
・複数の場合はEnterキーでサブミットできない※予測可能といいつつ、フォームの挙動は条件分岐が多くて分かりにくい気がする...
注意事項
- 上記の操作はすべてのブラウザでの動作を保証するものではありません。(Chrome v80.0.3987.132では動作しました)
- 上記のキーボード操作はあくまで標準のHTMLコンポーネントを使用した場合に機能します。 もし、コンポーネントを独自に実装している場合は上記の機能が無効化されている可能性があることに注意してください。
もし、補足や間違いなどありましたら、ぜひ教えてください。
参考記事
- 投稿日:2020-03-20T13:58:37+09:00
WebのUIにおけるキーボード操作まとめ
Webサイト/アプリケーションを実装する上で独自のフォーム部品を実装する場合、キーボード操作のサポートは意外と忘れられがちではないでしょうか?(これまで僕自身もあまり対応できていませんでした...)
今回は標準のHTMLコンポーネントがどのようにキーボード操作できるのかを把握し、自身でキーボード操作を実装する場合に参考となる情報をまとめました。なぜ、キーボード操作をサポートするのか
- 運動障害を持つ多くのユーザーはキーボードに依存している(これは健常者が怪我した場合も含まれる)
- 視覚障害のあるユーザーも通常、ナビゲーションにキーボードを使用する
- 一部のユーザーはキーボードを模倣した別のハードウェアを使う場合がある
- もしかしたら、マウスが壊れてキーボードしか使えない状況もありうる
- というか、普通にキーボードで操作できると便利じゃないですか?
WCAG 2.1の「ガイドライン2.1 操作可能」では以下のように記載されています。
すべての機能をキーボードから利用できるようにすること。
HTMLキーボード操作一覧
キーボード操作はアクセシビリティにおいて重要な部分です。キーボードを通してページを操作できるだけでなく、予測可能であることが重要です。これには一般的なキーボード操作の理解が必要になります。
次の表にはオンラインにおけるインタラクションの標準キーストロークおよび考慮すべき情報が記載されています。
インタラクション キーストローク Tab Enter SpaceBar 方向キー Esc 備考 要素移動 ・Tabによる前方移動
・Shift + Tabによる後方移動● ・フォーカスインジケータが表示されている必要がある
・移動の順序は論理的かつ直感的でなければいけないリンク Enter ● ボタン EnterもしくはSpaceBar ● ● role="button" の要素で両方のキーコマンドが有効であること チェックボックス SpaceBarによってチェックボックスのON/OFFを切り替える ● 複数のオプションを選択できる場合はチェックボックスを使用すること ラジオボタン ・方向キー(↑→↓←)でオプションを選択
・Tabで次のオプションに移動● ● 1つのオプションを選択できる場合はラジオボタンを使用すること ドロップダウンメニュー ・方向キー(↑↓)でオプション移動
・SpaceBarでメニューを展開● ● オートコンプリート ・入力時にフィルタリングを開始
・方向キー(↑↓)でオプションを移動
・Enterでオプションを選択● ● ダイアログ Escで閉じる ● ・モーダルダイアログはキーボードフォーカスを維持する必要がある
・モードレスダイアログはフォーカスを失うと閉じる
・ダイアログを閉じた場合、フォーカスは通常ダイアログを開いた要素に戻るスライダー ・方向キー(↑→↓←)で値の増減 ● ・ダブルスライダー(範囲選択)の場合はTab、Shift + Tabで両端を切り替える
・一部のスライダーではPageUp/PageDownにより増減できる
・Home/Endで開始/終了に移動する場合もあるメニューバー ・方向キー(↑↓)でオプションを移動
・方向キー(←→)でサブメニューの開閉
・Enterでオプションを展開もしくは選択● ● ・すべてのメニューにこれらのコントロールがあるわけではない。よりシンプルなメニューはTab/Enterに依存する タブパネル ・Tabでタブグループの中に入り、Tabでタブグループの外に移動する
・方向キー(↑→↓←)で前後のタブに移動● ● ・これは新しくロードせずにタブ移動できるタブ用
・タブが別のページへのリンクの場合はTab + Enterが適切
・方向キーを押すと、タブのコンテンツが自動で更新される(Enterキーは基本的に使わない)ツリーメニュー ・方向キー(↑↓)でオプションを移動
・方向キー(←→)でサブメニューの開閉もしくは1階層移動● スクロール ・方向キー(↑↓)で垂直にスクロール
・方向キー(←→)で水平スクロール
・SpaceBar / Shift + SpaceBarでページ単位でスクロール● ● 水平スクロールは最小限にする フォーム ・基本的にEnterでサブミット(備考参照) ● ・サブミットボタンがある場合はinput要素に限らず、Enterでサブミットできる
・Enterキーでサブミットしたくない場合はサブミットボタンをdisabledにする
・サブミットボタンが無い場合はinput要素の数が1つか複数かで挙動が異なる
・1つの場合はEnterキーでサブミットできる
・複数の場合はEnterキーでサブミットできないフォームのEnterキーによるサブミットの挙動を確認するためのWebページをこちらに用意しています。予測可能が重要といいつつ、フォームの挙動は条件分岐が多くて分かりにくい気がする...
注意事項
- 上記の操作はすべてのブラウザでの動作を保証するものではありません。(Chrome v80.0.3987.132では動作しました)
- 上記のキーボード操作はあくまで標準のHTMLコンポーネントを使用した場合に機能します。 もし、コンポーネントを独自に実装している場合は上記の機能が無効化されている可能性があることに注意してください。
もし、補足や間違いなどありましたら、ぜひ教えてください。
参考記事
- 投稿日:2020-03-20T06:51:48+09:00
初心者によるプログラミング学習ログ 267日目
100日チャレンジの267日目
twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。
100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。
267日目は、
おはようございます
— ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) March 19, 2020
267日目
・webサイト模写
・下層ページ1ページ目
・flexboxの並びがうまくできない#早起きチャレンジ#駆け出しエンジニアと繋がりたい#100DaysOfCode
- 投稿日:2020-03-20T01:19:25+09:00
A-Frameでカメラをトラッキング
A-Frame
バーチャルリアリティ体験を構築するためのオープンソースのWebフレームワークの一つです。
https://aframe.ioEntity Component System
A-FrameのEntity Component SystemのComponentは外観・動作・機能を構築するためにEntity上で組み合わせ・照合・および構成できるJavascriptモジュールです。
JavaScriptでComponentを登録し、DOMから宣言的に使用できます。
Componentは構成可能・再利用可能・および共有可能です。A-FrameアプリケーションのほとんどのコードはComponent内に存在する必要があります。
カメラをトラッキングするコンポーネントの作成
常にカメラの正面に移動させ、カメラの方向を向かせる機能のコンポーネント(camera-tracking)を作成し、文字とボックスのエンティティに組み込みます。
コード
HTML
index.html<html> <head> <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script> <script src="tracking.js"></script> </head> <body> <a-scene> <!--Camera--> <a-entity id="camera" camera></a-entity> <!--Field--> <a-sky color="#44AAFF"></a-sky> <a-plane position="0 0 -5" rotation="-90 0 0" width="6" height="6" color="#BB9944" shadow></a-plane> <!--Camera-tracking Text--> <a-entity id="text" camera-tracking="circleR: 2; shiftX: 1; shiftY: 0.1" text="font: mozillavr; value: Tracking test.; color: black" scale="3 3 1"> </a-entity> <!--Camera-tracking Box--> <a-entity id="box" camera-tracking="circleR: 3; shiftX: 0; shiftY: -0.5" geometry="primitive: box" scale="0.8 0.3 0.2" material="color: red" shadow> </a-entity> </a-scene> </body> </html>JavaScript
tracking.jsAFRAME.registerComponent('camera-tracking', { schema: { circleR: {type: 'float', default: '1'}, shiftX: {type: 'float', default: '0'}, shiftY: {type: 'float', default: '0'} }, tick: function () { var camera = document.querySelector('#camera'); var cameraPosition = camera.object3D.position; var cameraRotation = camera.object3D.rotation; // center position var cameraX = cameraPosition.x; var cameraY = cameraPosition.y; var cameraZ = cameraPosition.z; // rotation degree var degreePit = (cameraRotation.x * 180 / Math.PI); var degreeYaw = (cameraRotation.y * 180 / Math.PI); // circle size var r = this.data.circleR; // rotation point var positionY = Math.sin(degreePit / 180 * Math.PI) * r; var xzr = Math.cos(degreePit / 180 * Math.PI) * r; var positionX = Math.cos((degreeYaw + 90) / 180 * Math.PI) * xzr; var positionZ = Math.sin((degreeYaw + 90) / 180 * Math.PI) * -1 * xzr; // shift x value var addX = this.data.shiftX; var addXPosX = Math.sin((degreeYaw + 90) / 180 * Math.PI) * addX; var addXPosZ = Math.cos((degreeYaw + 90) / 180 * Math.PI) * addX; // shift y value var addY = this.data.shiftY; var addYPosX = Math.sin(degreePit / 180 * Math.PI) * Math.cos((degreeYaw + 90) / 180 * Math.PI) * -1 * addY; var addYPosY = Math.sin((degreePit + 90) / 180 * Math.PI) * addY; var addYPosZ = Math.sin(degreePit / 180 * Math.PI) * Math.sin((degreeYaw + 90) / 180 * Math.PI) * addY; // set properties this.el.setAttribute('rotation', degreePit +' '+ degreeYaw +' 0'); this.el.setAttribute('position', (positionX + cameraX + addXPosX + addYPosX)+' '+ (positionY + cameraY + addYPosY) +' '+ (positionZ + cameraZ + addXPosZ + addYPosZ)); } });解説
- 投稿日:2020-03-20T01:19:25+09:00
A-FrameでWebVRのカメラをトラッキング
A-Frame
バーチャルリアリティ体験を構築するためのオープンソースのWebフレームワークの一つです。
https://aframe.ioEntity Component System
A-FrameのEntity Component SystemのComponentは外観・動作・機能を構築するためにEntity上で組み合わせ・照合・および構成できるJavascriptモジュールです。
JavaScriptでComponentを登録し、DOMから宣言的に使用できます。
Componentは構成可能・再利用可能・および共有可能です。A-FrameアプリケーションのほとんどのコードはComponent内に存在する必要があります。
ECS(エンティティ・コンポーネント・システム)とは、主にゲーム開発で使用されているソフトウェアアーキテクチャパターンです。ECSは継承よりコンポジションの原則に従うことで、より柔軟にエンティティを定義することを可能にしています。
カメラをトラッキングするコンポーネントの作成
常にカメラの正面に移動させ、カメラの方向を向かせる機能のコンポーネント(camera-tracking)を作成し、文字とボックスのエンティティに組み込みます。
また、camera-trackingのプロパティとしてカメラからの「半径・X距離・Y距離」を与えることで画面レイアウトをしやすくします。コード
HTML
index.html<html> <head> <script src="https://aframe.io/releases/1.0.4/aframe.min.js"></script> <script src="tracking.js"></script> </head> <body> <a-scene> <!--Camera--> <a-entity id="camera" camera></a-entity> <!--Field--> <a-sky color="#44AAFF"></a-sky> <a-plane position="0 0 -5" rotation="-90 0 0" width="6" height="6" color="#BB9944" shadow></a-plane> <!--Camera-tracking Text--> <a-entity id="text" camera-tracking="circleR: 2; shiftX: 1; shiftY: 0.1" text="font: mozillavr; value: Tracking test.; color: black" scale="3 3 1"> </a-entity> <!--Camera-tracking Box--> <a-entity id="box" camera-tracking="circleR: 3; shiftX: 0; shiftY: -0.5" geometry="primitive: box" scale="0.8 0.3 0.2" material="color: red" shadow> </a-entity> </a-scene> </body> </html>JavaScript
tracking.jsAFRAME.registerComponent('camera-tracking', { schema: { circleR: {type: 'float', default: '1'}, shiftX: {type: 'float', default: '0'}, shiftY: {type: 'float', default: '0'} }, tick: function () { var camera = document.querySelector('#camera'); var cameraPosition = camera.object3D.position; var cameraRotation = camera.object3D.rotation; // center position var cameraX = cameraPosition.x; var cameraY = cameraPosition.y; var cameraZ = cameraPosition.z; // rotation degree var degreePit = (cameraRotation.x * 180 / Math.PI); var degreeYaw = (cameraRotation.y * 180 / Math.PI); // circle size var r = this.data.circleR; // rotation point var positionY = Math.sin(degreePit / 180 * Math.PI) * r; var xzr = Math.cos(degreePit / 180 * Math.PI) * r; var positionX = Math.cos((degreeYaw + 90) / 180 * Math.PI) * xzr; var positionZ = Math.sin((degreeYaw + 90) / 180 * Math.PI) * -1 * xzr; // shift x value var addX = this.data.shiftX; var addXPosX = Math.sin((degreeYaw + 90) / 180 * Math.PI) * addX; var addXPosZ = Math.cos((degreeYaw + 90) / 180 * Math.PI) * addX; // shift y value var addY = this.data.shiftY; var addYPosX = Math.sin(degreePit / 180 * Math.PI) * Math.cos((degreeYaw + 90) / 180 * Math.PI) * -1 * addY; var addYPosY = Math.sin((degreePit + 90) / 180 * Math.PI) * addY; var addYPosZ = Math.sin(degreePit / 180 * Math.PI) * Math.sin((degreeYaw + 90) / 180 * Math.PI) * addY; // set properties this.el.setAttribute('rotation', degreePit +' '+ degreeYaw +' 0'); this.el.setAttribute('position', (positionX + cameraX + addXPosX + addYPosX)+' '+ (positionY + cameraY + addYPosY) +' '+ (positionZ + cameraZ + addXPosZ + addYPosZ)); } });解説
- 投稿日:2020-03-20T00:52:23+09:00
JavaScriptで *タブ* を作成する方法 windowオブジェクト
タブとは何か?
タブとはドキュメントを切り替えて表示するためのGUIウィジェットである。一般的には長方形のボックス中にテキストラベルを表示する形で画面上部に表示され、タブの選択により管理するドキュメントを切り替えて表示させる仕組みとなっている。
wikipedia
https://ja.wikipedia.org/wiki/%E3%82%BF%E3%83%96_(GUI)<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <a href="javascript:window.open('sample2.html','','')">開く</a> </body> </html><!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title> </head> <body> <div class="top"> 新しいウィンドウ <!-- !ウィンドウを閉じる --> <a href="javascript:window.close()">閉じる</a> </div> </body> </html>なんとHTMLにワンライナーでの記述で完了です。
<a>
タグにjavascript:window.open('sample2.html','','')
記述します。
window.open
windowオブジェクトとはJavaScriptにあらかじめ用意されてるオブジェクトで、JavaScriptを何も書いてない状態でもwindowオブジェクトは利用する事が出来る。windowオブジェクトについて
https://developer.mozilla.org/ja/docs/Web/API/Window
('sample2.html','','')
1つめがファイル名、2つめがウィンドウ名、3つめがプロパティが入ります。簡単に記述できますね!
しかし、プライバシポリシー等で使用するケースは多いですよね!
是非開発の使ってみては?
- 投稿日:2020-03-20T00:18:38+09:00
永遠に続くクソ計算クイズ2 typescriptで
成果物
https://yuzuru2.github.io/neta1/dist/
リポジトリ
https://github.com/yuzuru2/yuzuru2.github.io/tree/master/neta1
UI
src/index.ts
// くそIEに対応させる import 'babel-polyfill'; class Calculation { private static instance: Calculation; // 秒数 private count = 15; // タイマーで使う private time_id; // シングルトン private constructor() {} // dom private readonly id_left = document.getElementById('left'); private readonly id_sign = document.getElementById('sign'); private readonly id_right = document.getElementById('right'); private readonly id_ans = document.getElementById('ans'); private readonly id_time = document.getElementById('time'); private readonly id_yes = document.getElementById('yes'); private readonly id_no = document.getElementById('no'); // 問題セット private set_question(left, sign, right) { this.id_left.textContent = left; this.id_sign.textContent = sign; this.id_right.textContent = right; } // 答えセット private set_answer(ans: string) { this.id_ans.textContent = ans; } // 足し算 private sum(ans: boolean) { const _left = Math.floor(Math.random() * 100) + 1; const _right = Math.floor(Math.random() * 100) + 1; this.set_question(_left, '+', _right); if (ans) { this.set_answer(`${_left + _right}`); return; } // 不正解作成パターン1 if (Math.floor(Math.random() * 2) === 0) { this.set_answer( `${_left + _right + (Math.floor(Math.random() * 3) + 1)}` ); } else { // 不正解作成パターン2 this.set_answer( `${_left + _right - (Math.floor(Math.random() * 3) + 1)}` ); } } // 引き算 private minus(ans: boolean) { const _left = Math.floor(Math.random() * 100) + 1; const _right = Math.floor(Math.random() * 100) + 1; this.set_question(_left, '-', _right); if (ans) { this.set_answer(`${_left - _right}`); return; } // 不正解作成パターン1 if (Math.floor(Math.random() * 2) === 0) { this.set_answer( `${_left - _right + (Math.floor(Math.random() * 3) + 1)}` ); } else { // 不正解作成パターン2 this.set_answer( `${_left - _right - (Math.floor(Math.random() * 3) + 1)}` ); } } // 掛け算 private multiplication(ans: boolean) { const _left = Math.floor(Math.random() * 10) + 1; const _right = Math.floor(Math.random() * 10) + 1; this.set_question(_left, '×', _right); if (ans) { this.set_answer(`${_left * _right}`); return; } // 不正解作成パターン1 if (Math.floor(Math.random() * 2) === 0) { this.set_answer( `${_left * _right + (Math.floor(Math.random() * 3) + 1)}` ); } else { // 不正解作成パターン2 this.set_answer( `${_left * _right - (Math.floor(Math.random() * 3) + 1)}` ); } } // 割り算 private division(ans: boolean) { let _left; let _right; while (1) { _left = Math.floor(Math.random() * 100) + 1; _right = Math.floor(Math.random() * 10) + 1; if (_left % _right === 0) { break; } } this.set_question(_left, '÷', _right); if (ans) { this.set_answer(`${_left / _right}`); return; } // 不正解作成パターン1 if (Math.floor(Math.random() * 2) === 0) { this.set_answer( `${_left / _right + (Math.floor(Math.random() * 3) + 1)}` ); } else { // 不正解作成パターン2 this.set_answer( `${_left / _right - (Math.floor(Math.random() * 3) + 1)}` ); } } // リセット private reset() { // タイマー処理 const time_method = () => { this.id_time.textContent = `${--this.count}`; if (this.count === 0) { alert('タイムアップ'); this.reset(); return; } this.time_id = setTimeout(time_method, 1000); }; clearTimeout(this.time_id); this.count = 15; this.time_id = setTimeout(time_method, 1000); // 時間セット this.id_time.textContent = `${this.count}`; // 正解・不正解どちらの問題を出すか決める const ans = Math.floor(Math.random() * 2) === 0; // 計算種類 const _sign = Math.floor(Math.random() * 4); switch (_sign) { case 0: this.sum(ans); break; case 1: this.minus(ans); break; case 2: this.multiplication(ans); break; case 3: this.division(ans); break; default: break; } // yesボタンを押したとき this.id_yes.onclick = () => { ans ? alert('正解') : alert('不正解'); this.reset(); }; // noボタンを押したとき this.id_no.onclick = () => { ans ? alert('不正解') : alert('正解'); this.reset(); }; } // シングルトン 単一のインスタンスを返す public static get_instance(): Calculation { if (!this.instance) { this.instance = new Calculation(); } // 生成済みのインスタンスを返す return this.instance; } // クイズをスタートする public start() { this.reset(); } } Calculation.get_instance().start();見返してみると、とんでもねーくそコードだな