Skip to content

Instantly share code, notes, and snippets.

@think49
Last active October 11, 2020 13:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save think49/fa8a4d3c3fd0bbcfedde622e9c67fce0 to your computer and use it in GitHub Desktop.
Save think49/fa8a4d3c3fd0bbcfedde622e9c67fce0 to your computer and use it in GitHub Desktop.
fired-event.js - GitHub gist

fired-event.js

概要

指定したイベント発火時、指定したkey/value形式の値にdataを書き換えます。

  • dataは登録された全イベントハンドラ間で共有されます
  • 一つのイベントタイプに複数のデータを登録できます

new FiredEvent(currentTarget, eventMap [, dataDescriptor])

下記引数を指定する事で「FiredEvent のインスタンス」を返します。

序数 引数名 説明
第1引数 currentTarget addEventListener で登録する要素ノードを指定します
第2引数 eventMap addEventListener の第一引数で指定するイベントタイプ(type)」「イベント発火時のデータ(key/value)を new Map 形式で指定します
第3引数 dataDescriptor data プロパティ参照値のdescriptorを {writable: false} 形式で指定します(既定値 = {writable: false})

dataDescriptor の設定値は以下の通りです。

名前 説明
writable Boolean値 true = 内部データの実体を返し、Map#set で書き換えが出来ます。/ false = 内部データの複製値を返し、Map#set で書き換えを行っても内部データに影響がありません

例えば、

  • focus イベント発火時 -> data.active = true を実行
  • blur イベント発火時 -> data.active = false を実行

を指定する場合、次のコードを書きます。

const fe = new FiredEvent(document.getElementById('sample'), new Map([
  ['focus', [['active', true]]],
  ['blur', [['active', false]]]
]));

第三引数が指定されなかった為、descriptorは {writable: false} として扱われます。詳細は「FiredEvent#data」を参照してください。

FiredEvent#data

FiredEvent で保存されたデータを new Map 形式で返します。

<input type="text">
<script>
'use strict';
const currentTarget = document.querySelector('input[type=text]');
const fe = new FiredEvent(currentTarget, new Map([
  ['focus', [['active', true]]],
  ['blur', [['active', false]]]
]));
console.log(Array.from(fe.data)); // [["active", undefined]]
console.log(fe.data.get('active')); // undefined
</script>

上記の通り、イベントが発火していない状態では初期値の undefined が返却されます。 fucus, blur イベントが発火すると、active 値が指定値に書き換わります。

currentTarget.dispatchEvent(new FocusEvent('focus', {bubbles: true}));
console.log(fe.data.get('active')); // true
currentTarget.dispatchEvent(new FocusEvent('blur', {bubbles: true}));
console.log(fe.data.get('active')); // false

new FiredEvent() の第三引数では、dataプロパティ参照値のdescriptorを指定でき、{writable: false} を指定すると、返り値が複製値となり、実質的に読み取り専用となります。

const currentTarget = document.querySelector('input[type=text]');
const fe = new FiredEvent(currentTarget, new Map([
  ['focus', [['active', true]]],
  ['blur', [['active', false]]]
]), {writable: false});
console.log(Array.from(fe.data)); // [["active", undefined]]
console.log(fe.data.get('active')); // undefined
console.log(fe.data === fe.data); // false

複製値ですので、Map#set で値を書き換えても、元データに影響がありません。

console.log(fe.data.get('active')); // undefined
fe.data.set('active', 'foo');
console.log(fe.data.get('active')); // undefined

第三引数に {writable: true} を指定する事で、元データに影響を与えることが出来ます。

const fe = new FiredEvent(currentTarget, new Map([
  ['focus', [['active', true]]],
  ['blur', [['active', false]]]
]), {writable: true});
console.log(fe.data.get('active')); // undefined
fe.data.set('active', 'foo');
console.log(fe.data.get('active')); // "foo"

FiredEvent#currentTarget

new FiredEvent の第一引数で指定された要素ノードを返します。

const currentTarget = document.getElementById('sample');
const fe = new FiredEvent(currentTarget, new Map([
  ['focus', [['active', true]]],
  ['blur', [['active', false]]]
]));

console.log(fe.currentTarget === currentTarget); // true
<!DOCTYPE html>
<title>fired-event.jsのデモ</title>
<style>
pre {
color: black;
background-color: #e9e9ff;
border: solid 1px #99f;
padding: 0.5em;
margin: 1em;
line-height: 1.2em;
}
</style>
<script src="./fired-event-1.0.2.js"></script>L
<h1>fired-event.jsのデモ</h1>
<h2>ダウンロード</h2>
<p>下記リンク先から、本ライブラリをダウンロードできます。</p>
<ul>
<li><a href="https://gist.github.com/think49/fa8a4d3c3fd0bbcfedde622e9c67fce0">fired-event.js - GitHub gist</a></li>
</ul>
<h2>サンプル</h2>
<p>以下のテキストボックスに「フォーカスを当てる」「フォーカスを外す」「マウスポインタを重ねる」「マウスポインタを範囲外へ移動する」を行ってみてください。</p>
<input id="sample" type="text">
<h2>データ出力</h2>
<p>サンプルに特定の動作を行う事で「<code>FiredEvent</code> に保存されたデータ」が出力されます(1行目は初期値です)。</p>
<pre><code id="output"></code></pre>
<script>
'use strict';
{
const currentTarget = document.getElementById('sample');
const eventMap = new Map([
['focus', [['active', true]]],
['blur', [['active', false]]],
['mouseover', [['mouse-state', 'over']]],
['mouseout', [['mouse-state', 'out']]]
]);
const fe = new FiredEvent(currentTarget, eventMap);
const listener = {
fe: fe,
handleEvent: function handleEvent (event) {
const header = event.type + ' event fired: data {';
const body = Array.from(this.fe.data).map(([key, value]) => key + ': ' + value).join(', ');
const footer = '}\n';
document.getElementById('output').textContent += header + body + footer;
}
};
for (let type of eventMap.keys()) {
currentTarget.addEventListener(type, listener, false);
}
listener.handleEvent({type: '[default]'});
}
</script>
/**
* fired-event-1.0.2.js
* Write data when the event fires.
*
* @version 1.0.2
* @author think49
* @url https://gist.github.com/think49/fa8a4d3c3fd0bbcfedde622e9c67fce0
* @license http://www.opensource.org/licenses/mit-license.php (The MIT License)
*/
'use strict';
const FiredEvent = (()=>{
const thisMap = new WeakMap;
return class FiredEvent {
constructor (currentTarget, eventMap, ...options) {
const pmap = Object.create(null);
const data = pmap.data = new Map;
const dataDescriptor = pmap.dataDescriptor = {writable: false, enumerable: true};
const descriptor = options[0];
if (Object(descriptor) === descriptor) {
dataDescriptor.writable = Boolean(descriptor.writable);
}
pmap.currentTarget = currentTarget;
pmap.eventMap = eventMap;
thisMap.set(this, pmap);
for (let [type, entries] of eventMap) {
for (let [dataKey] of entries) {
data.set(dataKey, void 0); // set default value
}
}
for (let type of eventMap.keys()) {
currentTarget.addEventListener(type, this, false);
}
}
get dataDescriptor () {
return Object.assign({}, thisMap.get(this).dataDescriptor);
}
get currentTarget () {
return thisMap.get(this).currentTarget;
}
get data () {
return this.dataDescriptor.writable ? thisMap.get(this).data : new Map(thisMap.get(this).data);
}
handleEvent (event) {
const pmap = thisMap.get(this);
const data = pmap.data;
const eventMap = pmap.eventMap;
for (let [dataKey, dataValue] of eventMap.get(event.type)) {
data.set(dataKey, dataValue);
}
}
};
})();
<!DOCTYPE html>
<title>fired-event.js</title>
<script src="./fired-event-1.0.2.js"></script>L
<input id="sample" type="text">
<script>
'use strict';
function isSameArrays (arrayA, arrayB) {
if (arrayA.length !== arrayB.length) return false;
for (let i = 0, len = arrayA.length; i < len; ++i) {
const arrayA2 = arrayA[i];
const arrayB2 = arrayB[i];
for (let j = 0, len2 = arrayA2.length; j < len2; ++j) {
if (!Object.is(arrayA2[j], arrayB2[j])) {
return false;
}
}
}
return true;
}
</script>
<script>
'use strict';
/**
* dataDescriptor = {writable: false}
**/
{
const currentTarget = document.getElementById('sample');
const fe = new FiredEvent(currentTarget, new Map([['focus', [['active', true]]]]));
console.assert(FiredEvent.length === 2);
console.assert(fe.currentTarget === currentTarget);
console.assert(fe.data !== fe.data);
console.assert(fe.data.get('active') === undefined);
fe.data.set('active', 'foo');
console.assert(fe.data.get('active') === undefined);
}
/**
* dataDescriptor = {writable: false}
**/
{
const currentTarget = document.getElementById('sample');
const fe = new FiredEvent(currentTarget, new Map([['focus', [['active', true]]]]), {writable: false});
console.assert(fe.data !== fe.data);
}
/**
* dataDescriptor = {writable: true}
**/
{
const currentTarget = document.getElementById('sample');
const fe = new FiredEvent(currentTarget, new Map([
['focus', [['active', true]]],
['blur', [['active', false]]],
['mouseover', [['mouse-state', 'over']]],
['mouseout', [['mouse-state', 'out']]]
]), {writable: true});
const data = fe.data;
console.assert(fe.data === fe.data);
console.assert(isSameArrays(Array.from(data), [['active', undefined],['mouse-state', undefined]]));
console.assert(data.get('active') === undefined);
console.assert(data.get('mouse-state') === undefined);
data.set('active', 'foo');
console.assert(isSameArrays(Array.from(data), [['active', 'foo'],['mouse-state', undefined]]));
console.assert(data.get('active') === 'foo');
console.assert(data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new FocusEvent('focus', {bubbles: true}));
console.assert(isSameArrays(Array.from(data), [['active', true],['mouse-state', undefined]]));
console.assert(data.get('active') === true);
console.assert(data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new FocusEvent('blur', {bubbles: true}));
console.assert(isSameArrays(Array.from(data), [['active', false],['mouse-state', undefined]]));
console.assert(data.get('active') === false);
console.assert(data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new MouseEvent('mouseover', {bubbles: true}));
console.assert(isSameArrays(Array.from(data), [['active', false],['mouse-state', 'over']]));
console.assert(data.get('active') === false);
console.assert(data.get('mouse-state') === 'over');
currentTarget.dispatchEvent(new MouseEvent('mouseout', {bubbles: true}));
console.assert(isSameArrays(Array.from(fe.data), [['active', false],['mouse-state', 'out']]));
console.assert(data.get('active') === false);
console.assert(data.get('mouse-state') === 'out');
}
/**
* dataDescriptor = {writable: false}
**/
{
const currentTarget = document.getElementById('sample');
const fe = new FiredEvent(currentTarget, new Map([
['focus', [['active', true]]],
['blur', [['active', false]]],
['mouseover', [['mouse-state', 'over']]],
['mouseout', [['mouse-state', 'out']]]
]));
console.assert(isSameArrays(Array.from(fe.data), [['active', undefined],['mouse-state', undefined]]));
console.assert(fe.data.get('active') === undefined);
console.assert(fe.data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new FocusEvent('focus', {bubbles: true}));
console.assert(isSameArrays(Array.from(fe.data), [['active', true],['mouse-state', undefined]]));
console.assert(fe.data.get('active') === true);
console.assert(fe.data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new FocusEvent('blur', {bubbles: true}));
console.assert(isSameArrays(Array.from(fe.data), [['active', false],['mouse-state', undefined]]));
console.assert(fe.data.get('active') === false);
console.assert(fe.data.get('mouse-state') === undefined);
currentTarget.dispatchEvent(new MouseEvent('mouseover', {bubbles: true}));
console.assert(isSameArrays(Array.from(fe.data), [['active', false],['mouse-state', 'over']]));
console.assert(fe.data.get('active') === false);
console.assert(fe.data.get('mouse-state') === 'over');
currentTarget.dispatchEvent(new MouseEvent('mouseout', {bubbles: true}));
console.assert(isSameArrays(Array.from(fe.data), [['active', false],['mouse-state', 'out']]));
console.assert(fe.data.get('active') === false);
console.assert(fe.data.get('mouse-state') === 'out');
}
</script>
@think49
Copy link
Author

think49 commented Oct 11, 2020

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment