TL;DR
こんなのを作りました!
ラジオボタンを🌼と🦋で装飾するライブラリです!
ずっとやってみたかったけど、できていなかった npmパーケージの公開 というものをやってみたので、そこで知り得た情報を共有するのがメインの記事です。
その他にも、 CSSアニメーションや、 JavaScriptの Custom Elements
、 Shadow DOM
について浅く触れています。
きっかけ
以前作った配色Webサービスで、ボタンに同じようなエフェクトをかけました。
(こっちの記事……、もう少しで 1000いいね
なんだ……。よかったらこっちも読んでね)
これが、なぜか自らの感性に突き刺さりまして、ラジオボタンとかにも適用できたら素敵じゃね? といった流れです。
それとは別に、npmパッケージってどうやって公開するんだろう? という疑問を解決したい欲求があったので、組み合わせてこの形になりました。
どういうパッケージなの?
ブラウザで使用する場合
<butterfly-radio name="flower">
<input type="radio" name="flower-color" id="red" value="1"><label for="red">Red flower</label>
<input type="radio" name="flower-color" id="green" value="2"><label for="green">Green flower</label>
<input type="radio" name="flower-color" id="blue" value="3"><label for="blue">Blue flower</label>
</butterfly-radio>
<script src="butterfly-radio.js"></script>
butterfly-radio.js
を読み込んで、ラジオボタンを <butterfly-radio>
タグで囲むだけのお手軽設計です。
(プログラミングをしたことのない人でも気軽に使えるくらいお手軽にしたかった)
Node.jsで使用する場合
$ npm install butterfly-radio
インストールして、
const butterflyRadio = require("butterfly-radio");
butterflyRadio();
インポートした関数を実行すれば、
<butterfly-radio name="flower">
<input type="radio" name="flower-color" id="red" value="1"><label for="red">Red flower</label>
<input type="radio" name="flower-color" id="green" value="2"><label for="green">Green flower</label>
<input type="radio" name="flower-color" id="blue" value="3"><label for="blue">Blue flower</label>
</butterfly-radio>
ブラウザの場合と同じように利用できます。
公開されている場所
npmパッケージとして公開済みですので興味のある方はどうぞ!
ブラウザで利用したいだけなら、GitHubからダウンロードして build
配下のJSファイルを読み込めばOKです!
実装関連の気付き
冒頭に書いたとおり、 CSSアニメーションであったり、 JavaScript周り、npmパッケージの公開にいたるまで、雑多にまとめられていますが必要に応じてご覧ください。
CSSアニメーション
配色Webサービスのときは anime.js
というライブラリを利用して作成していたのですが、依存ライブラリを持ちたくなかったのでCSSアニメーションに挑戦してみました。
(本当の本当に初心者なので大したことは書いてありません!)
…………っと思ったのですが、 内容薄かったので記事にするの止めましたッ!!
Custom Elements
ラジオボタンを装飾するライブラリをできる限り簡単に表現するために、
<ライブラリの専用タグ>
<input type="radio" name="flower-color" id="red" value="1">
<label for="red">Red flower</label>
</ライブラリの専用タグ>
みたいな感じで、 <ライブラリの専用タグ>
で囲むだけでできたらなぁ、という考えを持っていました。
色々と調べていると、 Custom Elements
というJavaScriptを用いて専用タグをつくるAPIを見つけたわけです。
Custom Elements is 何?
Custom Elements は、独自のカスタム HTML 要素を作成するための機能です。 Custom Elements は、独自のスクリプト動作と CSS スタイリングを持つことができます。これは web components 一部ですが、単独でも使用できます。
まさに、やりたかったことを実現してくれるAPIでした!
使い方
カスタム要素はJavaScriptの class
を利用して作成します。
例えば、
<butterfly-radio name="hoge"></butterfly-radio>
というカスタム要素を作るために ButterflyRadio
クラスを作るとすると……
class ButterflyRadio extends HTMLElement {
constructor() {
super();
}
static get observedAttributes() {
return ['name'];
}
attributeChangedCallback(attrName, oldVal, newVal) {
const name = this.getAttribute('name');
// 独自タグのname属性を用いた処理
}
}
のような感じで定義できます。
HTMLElement
を extends
することでHTML要素に必要な関数を実装することができます。
関数・プロパティ | 概要 |
---|---|
constructor | コンストラクター。要素の作成またはアップグレード時に呼び出されます |
observedAttributes | 変更を監視する属性を決定します。上の例では name 属性に対して監視を行なっています。 |
attributeChangedCallback | 要素の属性が変更、追加、削除、または置換されたときに呼び出されます。 |
Custom Elements
や HTMLElement
には他にも様々な機能がたくさんありますが、今回作った <butterfly-radio>
では、この3つを主に利用しています。
constructor
<butterfly-radio>
タグ内には <input>
タグと <label>
タグしかなく、アニメーションするための画像やスタイルがありません。
そのため、コンストラクターでアニメーション用のスタイルを Shadow DOM
を用いてカスタム要素内のみに適用しています。
( Shadow DOM
については後述)
constructor() {
super();
this._shadowRoot = this.attachShadow({mode: 'open'});
this._shadowRoot.innerHTML = `
<style>
:host {
position: relative;
}
.butterfly {
position: inherit;
top: -3em;
width: 2.5em;
height: 2.5em;
transition: all 1000ms 0s ease-in;
}
// any...
</style>
`;
}
observedAttributes
上のコードのまんまです。
static get observedAttributes() {
return ['name'];
}
<butterfly-radio>
タグで使用している name
属性を返すように実装しています。
attributeChangedCallback
ラジオボタンがクリックっされたときの振る舞いを実装しています。
attributeChangedCallback
は監視対象の name
属性の変更を検知するので、実質的には、初回のみ呼ばれることになります。
その子要素であるラジオボタン( <input>
要素)に対しては addEventListener
を実装することで、イベントハンドラを定義していきます。
また、アニメーション用の画像の作成・挿入も、ここで、ラジオボタン単位に行なっています。
attributeChangedCallback(attrName, oldVal, newVal) {
const name = this.getAttribute('name');
Array.from(this.children).forEach((el) => {
if (el instanceof HTMLInputElement) {
if (el.type === 'radio') {
// ラジオボタンに対してclass属性などを付与
// Make butterfly image.
const _img = document.createElement('img');
// 作成した🦋画像に対してclass属性などを付与
// Add change event to radio.
el.addEventListener('change', (event) => {
if (el === event.target) {
if (event.target.value) {
// ラジオがONになったときのイベント
}
}
}, false);
// Add elem to shadow root.
this._shadowRoot.appendChild(_img);
this._shadowRoot.appendChild(el);
}
}
// Add label to shadow root.
if (el instanceof HTMLLabelElement) {
// ラベルに対して必要に応じて属性などを付与
this._shadowRoot.appendChild(el);
}
});
}
カスタム要素を定義する(利用可能に)
クラスを作成しただけではただのクラスオブジェクトに過ぎず、 <butterfly-radio>
タグとしては利用できません。
利用可能な新たな要素として登録( define
)することで、カスタム要素の利用が可能になります。
customElements.define('butterfly-radio', ButterflyRadio);
参考にさせていただいたサイト
Custom Elements v1で独自のHTML要素を定義する | Subterranean Flower Blog
Shadow DOM
今回はライブラリということもあり、その他の資産に影響を与えたくない……、つまりはスコープを最小限にしたい、という気持ちがありました。
そこで初めて知ったのが、 Shadow DOM
というAPIです。
Shadow DOM is 何?
Web コンポーネントにおいてカプセル化 (構造やスタイル、挙動を隠し、同じページの他のコードと分離すること) は重要です。これにより他の場所でのクラッシュを防ぎ、またコードが綺麗になります。Shadow DOM API はこの隠され分離された DOM を付加するための方法を提供しています。この記事では Shadow DOM を使う基本を記述しています。
まさに、やりたかったことを実現してくれるAPIでした!(2回目)
使い方
Shadow DOM
は attachShadow
メソッドを利用することで追加できます。
先程のコンストラクター内では、
constructor() {
super();
this._shadowRoot = this.attachShadow({mode: 'open'});
// any...
}
といった感じで Shadow DOM
を追加しています。
ちなみに、 Shadow DOM
が追加された側(ここでは <butterfly-radio>
要素)をHost(ホスト)と呼び、追加した Shadow DOM
のことをShadow Root(シャドウルート)と呼ぶそうです。
シャドウルートを追加したら、あとはスタイルや要素を追加していくことで、隠された空間(シャドウ)にDOMを追加したり、操作したりできます。
let shadowRoot = this.attachShadow({mode: 'open'});
shadowRoot.innerHTML = `
<style>
:host {
position: relative;
}
</style>
`;
このように :host
にスタイルを用意すればHostに適用することもできます。
<butterfly-radio>
のコードでは、 attributeChangedCallback
メソッドの各所で、
this._shadowRoot.appendChild(el);
を行なっていました。
ラジオボタン、ラベル、🦋画像、のそれぞれの要素を、全て this._shadowRoot.appendChild(el);
でシャドウルートに追加しているわけです。
ここで実際に生成されたカスタム要素内の Shadow DOM
を見てみましょう。
<butterfly-radio>
要素の直下に #shadow-root (open)
ができていて、その配下にラジオボタンなどの要素ができています。
#shadow-root (open)
内の <style>
タグを開くと、
constructor
でシャドウルートに追加したスタイルがバッチリ表示されます。
ここでしか適用されないスタイルの出来上がりです!
参考にさせていただいたサイト
Shadow DOM v1でHTMLの内容と構造を分離する | Subterranean Flower Blog
npmパッケージ
アカウント作成
何をするにもアカウントがないとできません。
Sign Up ページからサクッとアカウントを作ってしまいましょう!
package.jsonの作成
npmパッケージたる所以、 package.json
を作成します。
これは手作りするのではなく、
$ npm init
コマンドを実行すれば、対話形式で作成することができます。
あまり詰まるところはないと思いますが、 test command
はテストの仕方がよくわからない(下の方でも書いている)のもあり、デフォルトで作成したあとにファイル編集で削除しています。
(つまりは任意項目)
エクスポート用のJSファイルを作成
npmパッケージ、つまりはNode.js配下で動くスクリプトの場合、ライブラリを require("butterfly-radio")
のようにインポートして利用します。
その際は、 ./butterfly-radio/index.js
を参照するようになっていて、それができるようにNode.js用のエクスポートを行った index.js
ファイルを作成します。
// モジュールを定義(オブジェクトそのものを返すだったり、関数を返すだったり、ライブラリによってまちまち)
const myModhle = () => hoge();
// モジュールをエクスポート
module.exports = myModhle;
hoge()
のところで ./lib
配下の別JSファイルを require
して本実装は ./lib
の配下にまとめてしまうこともあるそうです。
私の場合は上の章『Custom Elements』で書いたように、
const butterflyRadio = () => customElements.define('butterfly-radio', ButterflyRadio);
module.exports = butterflyRadio;
ButterflyRadio
クラスをカスタム要素として登録する関数をモジュールとして定義しています。
npmに公開
細かいことを言うと、たくさんやることがあります(下記『参考にさせていただいたサイト』を参照するとわかりやすいです)が、最小限の構成だと、このタイミングでnpmに公開ができます。
$ npm publish ./
作成したライブラリのトップディレクトリ( package.json
があるところ)で実行すればOKです。
npmパッケージの更新(バージョンアップ)
例えばライブラリに更新を加えるなど、npmパッケージもバージョンアップをしたい場合があります。
その場合は、
$ npm version patch
v1.0.1
npm version patch
コマンドを利用して、パッケージのバージョンアップを行いましょう。
その後に、
$ npm publish ./
npm publish
すれば最新バージョンを公開できます!
ちなみに……
マイナーバージョンアップ(v1.0.0 => v1.1.0)や、メジャーバージョンアップ(v1.0.0 => v2.0.0)などは、別のコマンドで行うことができます。
$ npm version minor
v1.1.0
$ npm version major
v2.0.0
gitリポジトリをタグ付けしておくと便利
npmパッケージの公開、バージョンアップに合わせてタグ付けをしておくと良いです。
$ git tag
v1.0.1
$ git push origin tags/v1.0.1
git tag
コマンドで自動的にバージョンごとのタグを作ってくれるようです。
まだわからない点
テストのやり方が、いまいちわかりませんでした。
macha
とかでやるのか? とか、
そうなったらライブラリにテストコードは含めるのか? とか、
です
このあたりはしっかりと調べて、READMEにバッジを貼れるようにしたいなと思っています。
参考にさせていただいたサイト
このライブラリの微妙なところ
ラジオボタンのアニメーションを固定時間にしているために、すばやく切り替えると🦋が不在になることがあります(笑)
🌼は色がついているので、ONとOFFを間違えることはないですが、正直微妙です。
CSSアニメーション超初心者なので、調整する方法を引き続き思案していこうと思います!
あとはカスタム要素内の name
属性……。
付けては見たものの、いらないよね? 消そうかな(笑)
まとめ
ずっとJavaメインでバックエンドばかりやっていた私が、前回のWebサービスや、今回のnpmパッケージと、フロントエンドに挑戦してみました。
(技術面が乏しいため記事の内容が薄いかもしれませんが……、寛大な目で見ていただけると嬉しいです )
やはり コード書いたらすぐに動く というのはモチベーションを維持するという意味でも良いですよね!
『フロントは難しそうだから……』と尻込みする気持ちもわかりますが、 簡単なものを作って、それが動く姿を見ればテンション激上がり だと思うので、気軽にやってみてください!
Thanks!
最後まで読んでいただき、ありがとうございました!!
タイトルのように『母の日』に……、とはなかなかいかないと思いますが、ブログやWebサイトを🌼や🦋 で装飾してみたいという方は、ぜひ使ってみてください!