input[type=range]で範囲指定っぽいUI
input[type=range]要素を組み合わせると、なんちゃって範囲指定のUIが構築できそうなのでやってみた。
開始値を指定するスライダーと、終了値を指定するスライダーが連動して、開始値が終了値より先にいったりしないよう制限もかけている。
実際に動作するサンプルはStackblitzで作ったページを参照: https://react-ts-keurq5.stackblitz.io
大事なのはdatalist要素で範囲を定義しておくこと。 単純にinput[type=range]要素でmax属性とmin属性で最大値・最小値または連動するように互いのvalueを参照し合うよう指定しても、想像した任意の値の範囲を指定するようなUIにはならない。 正確に言えば、開始値が終了値を追い越さないようになっているし開始値と終了値を指定する機能自体は満たしているけど、スライダーの値の範囲がぐちゃぐちゃ可変してしまって使いづらい。
そこで、datalistで値の範囲を定義して、2つのinput[type=range]要素がどちらも同じ値の範囲を持つスライダーになるようにした。 開始値のスライダーが終了値のスライダーより大きい値にならないように仕込むには、max,min属性を使っていけなくて、onChangeイベントで引っ掛ける必要があった。
以下にコードを貼り付けておく。 今回はReactで作ったが、やっていることは単純なので、他のフレームワークや素のJavaScriptでもすぐ書ける。
import React, { Component } from "react"; import { render } from "react-dom"; import "./style.css"; interface AppProps { startAt: number; endAt: number; callback?: (range: AppState) => any; } interface AppState { startAt: number; endAt: number; } class App extends Component<AppProps, AppState> { constructor(props) { super(props); this.state = { startAt: this.props.startAt, endAt: this.props.endAt }; } render() { const onChangeStart = (event: React.ChangeEvent<HTMLInputElement>) => { const startAt = parseInt(event.target.value); // 終了値を超えないように if (startAt < this.state.endAt) { this.setState({ startAt }, () => { this.props.callback(this.state); }); } }; const onChangeEnd = (event: React.ChangeEvent<HTMLInputElement>) => { const endAt = parseInt(event.target.value); // 開始値を下回らないように if (endAt > this.state.startAt) { this.setState({ endAt }, () => { this.props.callback(this.state); }); } }; // option要素で値の範囲を定義しないと、範囲が固定にならない。 const options = []; for (let i = this.props.startAt; i <= this.props.endAt; i++) { options.push(<option key={i} value={i} />); } return ( <div> <h1>input[type=range]で範囲指定っぽいUI</h1> <p>スライダーをドラッグして開始と終了をそれぞれ指定する</p> <datalist id="datalist">{options}</datalist> <p> 値の範囲: {this.state.startAt} 〜 {this.state.endAt} </p> <div> 開始: <input type="range" list="datalist" min={this.props.startAt} max={this.props.endAt} value={this.state.startAt} onChange={onChangeStart} /> <br /> 終了: <input type="range" list="datalist" min={this.props.startAt} max={this.props.endAt} value={this.state.endAt} onChange={onChangeEnd} /> </div> </div> ); } } const logging = value => { console.log(value); }; render( <App startAt={0} endAt={120} callback={logging} />, document.getElementById("root") );
stackblitz上のソースコードは https://stackblitz.com/edit/react-ts-keurq5?file=index.tsx
ルンバe5を買ってしばらく経ったのでまとめ
TL;DR
- 明るい色のラグを買え。
2月くらいにルンバe5を購入しました。基本的に満足しているのですが、いくつか課題もあったのでまとめてみます。
ラグ・絨毯の色には気をつけろ
ルンバを買ったのと近い時期に暗めの色のラグを買ってしまうという、ルンバビリティを一番下げるミスをしてしまいました。 そして買って早々に落下センサー部をアルミホイルで隠すというハックをする羽目に。
ルンバe5を買ったが、これまた先日購入したばかりのラグが暗い色なので段差センサーにひっかかりラグの上を掃除してくれない問題に遭ったが、落ち着いてセンサーをアルミホイルで隠して事なきを得た。もちろん普通の段差に落ちるようになった。
— りんご🍏 (@mstssk) February 29, 2020
参考: https://t.co/MWGEIPmu63
ラグの色はこんな感じです。このくらいの色でもルンバは段差だと認識しちゃうようです。
センサーの隠し方は少し試行錯誤のした結果、こういう形に落ち着いています。
- 幅1cmくらいのアルミホイルで覆うだけ。横からは光が入るようになっていないといけない。
- センサーを完全に隠してはいけない。真っ暗になり常に段差の上にいる判定になりルンバが動かない。
- お手入れの際にセンサー部をどのご家庭にもあるエアダスターで掃除する。
- 覆った分、ホコリ等が挟まって汚れやすくなる。汚れて暗くなり段差と誤検知することもある。わざと残した横の隙間からエアダスターでホコリを吹き飛ばす。
もちろん、段差検知のためのセンサーを隠しているので、段差に落ちるようになります。 うちの場合、玄関の段差に落ちるようになったので、バーチャルウォールで回避しています。
あと、センサーを隠しているせいなのかいまいちわかりませんが、Dirt Detectが動作していない気もします。2ヶ月立ちますが、一度もありません。我が家がそこまで汚くないだけなのかどうか…
1,2cmちょっとの段差なら乗り越える
ので、越えてほしくないところに物を置いたりしないように。
カラーボックスの1番下の段にビニール紐を置いていたら、そこにルンバが突っ込んで巻き込んで止まってました。
わたし魔女のキキ!
— りんご🍏 (@mstssk) March 25, 2020
これはビニール紐を食べたルンバ pic.twitter.com/gM5nEApfUb
また、こういうリクライニングチェアの足に乗り上げて止まってしまうのも想定外でした。 今は足に黒い紐を張ってルンバが乗り上げないように回避しています。
バーチャルウォールよりそもそも床に物を置かないようにする
バーチャルウォールが充電ベースに向いていなくても、近くにあると干渉することがあるようです。ルンバがいまいちベースに帰ってくれない場合、バーチャルウォールの位置を変えてみるとよいと思います。
ホームベースから2.5 m以内に デュアルバーチャルウォールや他のルンバのホームベースなどが設置されていないことを確認してください。これらの機器の赤外線が干渉する場合があります。 ルンバがホームベースに戻らない。
そもそもバーチャルウォールをできるだけ使わないほうがよいと思っています。 とはいえ棚が足りないとか配線の都合で電源タップや一部機器が床置きになっているので、無印良品のワイヤーバスケットにとりあえず入れることでルンバを回避したりしています。
18ー8ステンレスワイヤーバスケット2 約幅37×奥行26×高さ8cm 通販 | 無印良品
家にいる時に動かさないほうがよい
購入後は平日昼間の会社にいる最中にルンバに動いてもらうようスケジュール設定していました。 しかし最近は自宅勤務が続いています。
ルンバが動いている最中も気にせず仕事したりしていたんですが、ラグの上を掃除する時に流石にホコリがそれなりに舞うようで、咳き込むようになりました。
今は買い物やジョギングのため外出する時に動かすようにしています。 Googleアシスタントの外出時ルーティンにカスタムコマンドで「掃除して」と仕込んでいます。
全日本ラジオボタンのラベルにクリック判定を付けろ協会
全日本ラジオボタンのラベルにクリック判定を付けろ協会 会長の id:mstssk です。
皆さんはチェックボックスやラジオボタンのラベル(キャプション)をクリックしても選択したことにならず憤慨したことはありませんか?
私はあります。
(追記)タイトルはラジオボタンなのに記事はチェックボックス主体で書いているのはただのミスです😇
よくないチェックボックス
Webサイトでこういうチェックボックスによる選択肢をよく見かけます。
<input type="checkbox"> 選択肢1 <input type="checkbox"> 選択肢2
そして大抵の場合、視覚的にチェックボックスと一体になっている「選択肢x」というラベルをクリックしても何も起きないということがよくあります。
こういう実装はアクセシビリティの問題があります。
まず、単純にチェックボックスを選択しづらいということ。
そして、チェックボックスとそのラベルの一体というセマンティクスをHTML上表現していないため、視覚障害者などの読み上げツールから適切に認識されません。
よいチェックボックス
チェックボックスとラベルのセットを表現するには<label>
要素を使います。
HTMLだけで実現できます。
JavaScriptもCSSもいりません。
<input>
要素を<label>
要素でくくるだけでOKです。
<label><input type="checkbox"> ラベルをクリックしてもチェックできる</label>
<input>
要素のclickイベントで何かしら処理を走らせている場合でも問題ありません。ラベル部分をクリックしても<input>
要素のclickイベントは発火します。
デザインなどの都合により<label>
要素でくくれない場合、for
属性とid
属性を使います。
<input type="checkbox" id="checkbox01"> <label for="checkbox01">ラベルをクリックしてもチェックできる</label>
チェックボックスだけでなくラジオボタンでも同じことができます。 いくつかのパターンを書いたサンプルページを用意しました。
Macでfn+7をF7にする方法
仕事で使っているMacBook Proのバタフライキーボードのチャタリングが酷すぎる。 Unshakyを使ってみたりいているんですが、緩和策でしかないのでやっぱり誤入力が度々起こってしまい、どうしようもない。
HHKBあたりを衝動的に買ってしまおうかと思ったのですが、fn+数字キーによるFunctionキー入力が少々ネックだと感じて、Amazonの購入確定ボタンをまだ押していません。 *1
PFU HHKB Professional HYBRID Type-S 日本語配列/墨 PD-KB820BS
- メディア: エレクトロニクス
Karabinerでなんとかする
いっそバタフライキーボード使っている時を含めて普段からfn+数字キーで入力する状況に自分を矯正してしまえばいいのでは?と考えました。
KarabinerというMacのキー入力を自由にカスタマイズできるツールで対応できます。 *2
インストールガイドに従い、インストールとMacのシステムの入力監視への操作権限を付与したら、使えるようになります。
- Karabinerのサイトでfn+数字キーの置換ルールを検索
Map fn + number keys to function keys (rev 2)
というルールセットをImport- インポート後のKarabinerの設定画面が開くので、
Map fn + number keys to their corresponding function keys (rev 1)
をEnable
輝度設定やメディア再生コントロールが動作しちゃう場合
Macのキーボード設定で「F1、F2などを標準のファンクションキーとして使用」を有効にしていると、 Map fn + number keys to function keys (rev 2)
ルールではFunctionキー入力になってくれません。
替わりに Map fn + number keys to their corresponding media control keys (rev 2)
をEnableにすればちゃんとfn+数字キーがFunctionキー入力になってくれます。
設定のJSONファイルの中身を見ると理由がわかります。 このルールが実際に行っているのは次のとおりです。
Map fn + number keys to their corresponding function keys (rev 1)
:- fn+1 → F1+fn
Map fn + number keys to their corresponding media control keys (rev 2)
:- fn+1 → F1
つまり、「F1、F2などを標準のファンクションキーとして使用」が有効になっていない状態のキーマップを想定した置換ルールだからでした。
追記: 別のショートカットキー
ctrl+i に体を矯正したほうがよさそう。
マイナーだけど便利なIME系ショートカット。以外と知らない人多い pic.twitter.com/jpew7UUUyR
— まやとん@Alice (@alice_book) 2020年4月18日
video要素のcontrols属性のブラウザごとの動作
2020/03/22現在はほぼ全てのブラウザでvideo要素のcontrols属性をサポートしている。
でもどうせブラウザによって動作が違うんでしょ、と思って調べました。
https://caniuse.com/#feat=mdn-html_elements_video_controls
サンプルコード
動作確認のため作ったページ: Example:`controls` attr of video element.
動画は🍏の最近のSplatoon 2のプレイ映像。
スクリーンショット
左上からZ字の順でSafari, Firefox, Chrome, Edge。
初期表示 | 再生して1秒後の表示 |
---|---|
ブラウザ別の機能差分表
環境: macOS Catalina 10.15.3
Chrome 80.0.3987.149 | Firefox 74.0 | Safari 13.0.5 | |
---|---|---|---|
再生/一時停止ボタン | あり | あり | あり |
音量コントロール | あり | あり | あり |
全画面表示ボタン | あり | あり | あり |
再生時間表示 | 現在/総再生時間 | 現在/総再生時間 | 現在/残再生時間 |
Picture in Picture | あり(ハンバーガーメニューから ※Android ChromeではN/A) |
あり(コンテキストメニューから) | あり |
15秒進む/戻るボタン | N/A | N/A | あり |
Chromecast等へのキャスト | あり(コンテキストメニューから ※Android Chromeではハンバーガーメニューから) |
N/A | N/A |
ダウンロード ※"名前を付けて保存"といったメニューではなくあくまで「ダウンロード」メニューの有無 |
あり(ハンバーガーメニューから) | N/A | あり(コンテキストメニューから) |
再生速度変更 | N/A | あり(コンテキストメニューから) | N/A |
コントロールの表示/非表示切り替え | あり(コンテキストメニューから) | あり(コンテキストメニューから) | あり(コンテキストメニューから) |
video要素クリック | 再生/一時停止トグル | 再生/一時停止トグル | 再生/一時停止トグル |
video要素フォーカス時スペースキー押下 | 再生/一時停止トグル | 再生/一時停止トグル | N/A |
video要素フォーカス時左右カーソルキー押下 | 押している間、早回し/早戻し | 押すと15秒進む/戻る | N/A |
真似しちゃいけないdeployステップの使い方
これはCircleCI Advent Calendar 2019の8日目の記事です。
こんにちは、CircleCI大プッシュしてたらCircleCIの回し者だと思われた@mstsskです。
今回はdeploy
ステップの変な使い方を思いついたので紹介します。
ただし、真似しちゃいけません。
deploy
ステップについて
CircleCIでは、CIジョブで実行するコマンドのステップをrun
というキーワードで記述します。
そして実はdeploy
というキーワードでもコマンドを記述できるようになっています。
deploy
ステップの書き方はrun
と同じですが、文字通りデプロイのためのステップであり、動作が異なります。
parallelism
を使ったジョブの場合、deploy
ステップは他のすべてのノードが成功した場合にのみ、ノード番号 0 として実行されます。 ノード番号 0 以外はステップを実行しません。引用元: https://circleci.com/docs/ja/2.0/configuration-reference/#deploy
deploy
ステップを使ったconfig.ymlは次のような感じになります。
# .circleci/config.yml version: 2.1 jobs: build: docker: - image: circleci/ruby parallelism: 4 # 4並列で実行 steps: - checkout - run: bundle install - run: # circleciコマンドを使ってテストを4分割して実行 name: Parallel Testing command: bundle exec rspec $(circleci tests glob spec/**/*.rb | circleci tests split) - deploy: # テストが通ったらデプロイ。このステップは4つのノードのうち0番目でだけ実行される name: Deploying to dev env command: bundle exec cap dev deploy
つまり、deploy
ステップを使うとCircleCIのジョブの中で並列処理の待ち合わせができるんです!
待ち合わせ処理を書いてみる
並列実行している処理の待ち合わせが書けるなら、あとはデータの受け渡しができれば、もう並列処理のプログラム書けるようなもんだよね? 例えば、大きな処理の分割実行とその結果の統合が1つのジョブの中で済ませられるのでは?と考えてやってみました。
deploy
ステップで並列実行の待ち合わせをして、各ノードの結果をCircleCIのキャッシュ経由で受け渡しするサンプルは次のとおりです。
# .circleci/config.yml version: 2.1 jobs: build: docker: - image: circleci/node:12 parallelism: 4 steps: - checkout # 成果物を result-{ノード番号}.txt に保存する、ビルドとかテスト実行など並列実行したい遅い処理 - run: name: Building Some Results command: | mkdir results/ && ./dummy_slow.sh > results/result-$CIRCLE_NODE_INDEX.txt # 成果物をキャッシュに保存 - save_cache: key: results-{{ .BuildNum }}-{{ .Environment.CIRCLE_NODE_INDEX }} paths: - results/ # 各ノードのビルド実行・キャッシュ保存を待つ - deploy: name: Waiting Results command: ":" # 各ノードの成果物をキャッシュから取り出す - restore_cache: { keys: ["results-{{ .BuildNum }}-1"] } - restore_cache: { keys: ["results-{{ .BuildNum }}-2"] } - restore_cache: { keys: ["results-{{ .BuildNum }}-3"] } # 0番目のノードで他ノードの成果物を組み合わせる - deploy: name: Merging Results command: | ls results/ echo cat results/result-*.txt
restore_cache
のところがどうしても冗長な書き方になりますが、これで1つのジョブの中で並列実行した結果を統合できます。
もうちょっとconfig.ymlを整理したサンプルは↓。
https://circleci.com/gh/mstssk/circleci-deploy-step-play github.com
何に使うの?
もともとは、並列テストのカバレッジデータをサクッと統合したいと思ってなんとかジョブの中でミニマムに解決できないかと考えて思いついたものです。
実際にジョブの中でデータ集めができることを確認したあたりで正気に帰りました。 本来の使い方とは違うやり方だけど試行錯誤してなんとかなったぜやったぁ状態になる瞬間ってたまにありますよね。
よっぽど1つのジョブの中で完結させたい理由がもしあったなら使えるテクニックかもしれませんが、本当に必要かどうか3回考えてから諦めましょう。
普通に別ジョブにしてpersist_to_workspace
でデータ受け渡してやりましょう。
カバレッジデータの統合をしたい場合は、各カバレッジツールがCircleCIの場合の使い方サンプルを公開してたりしますし、Coverallsは結果をよしなにマージしてくれる機能があるらしいです1。
width属性の詳細度は要素セレクタよりも低い
img要素などいくつかのHTML要素では width
と height
を属性で書いてもよい。
<img width="100px" src="foobar">
しかし、このwidth属性に対してCSSでもwidth指定をしていたら、どちらが優先されるのか。
<img width="100px">
と <img style="width:100px;">
は挙動が違うというのはわかって入るが、具体的にどうなんだろうと思って調べてみた。
ざっくり調べた結論としては、属性でwidthを指定するのは何よりも優先度(詳細度)が低いというつもりでいればいいだろう、という感じ。
width属性はCSSじゃないので詳細度という言い方が適切かはわからない。
あと、スタイルと一緒くたにしてしまったが、本来はスタイルの指定に使うものではない。 HTML5.2のspecを一部読んだ感じでは、width属性とheight属性両方とも同時に指定するのが本来の仕様であり、ユーザーエージェント(ブラウザ)はその値を描画のヒントとすることを期待するものとのこと。
実際のブラウザの挙動を確認するのはstackblitzで適当にサンプルコード書いた。 Safari, Chrome, Firefoxあたりで確認して、同じだった。
https://stackblitz.com/edit/attribute-style-specificity