いくつかのメソッドを持つオブジェクトを作成しました。これらのメソッドの一部は非同期であるため、メソッドが完了したときにイベントを使用してアクションを実行できるようにします。これを行うために、オブジェクトにaddEventListenerを追加しようとしました。
var iSubmit = {
addEventListener: document.addEventListener || document.attachEvent,
dispatchEvent: document.dispatchEvent,
fireEvent: document.fireEvent,
//the method below is added for completeness, but is not causing the problem.
test: function(memo) {
var name = "test";
var event;
if (document.createEvent) {
event = document.createEvent("HTMLEvents");
event.initEvent(name, true, true);
} else {
event = document.createEventObject();
event.eventType = name;
}
event.eventName = name;
event.memo = memo || { };
if (document.createEvent) {
try {
document.dispatchEvent(event);
} catch (ex) {
iAlert.debug(ex, 'iPushError');
}
} else {
document.fireEvent("on" + event.eventType, event);
}
}
}
iSubmit.addEventListener("test", function(e) { console.log(e); }, false);
//This call is added to have a complete test. The errors are already triggered with the line before this one.
iSubmit.test();
これはエラーを返します:Failed to add eventlisterens: TypeError: 'addEventListener' called on an object that does not implement interface EventTarget."
これで、このコードはphonegapアプリで使用され、実行するとAndroid/iosで動作します。ただし、テスト中に、少なくとも1つのブラウザーで動作するようになればいいですね。
PS>バブリングを有効にしてドキュメントルートをリッスンできることはわかっていますが、各オブジェクトが単独で機能する場所を少しだけOOPとする必要があります。
addEventListener
は、特定のイベント関連インターフェイスを実装するDOM要素を対象としています。純粋なJavaScriptオブジェクトのイベントシステムが必要な場合は、カスタムイベントシステムを探しています。例は、Backbone.jsの_Backbone.Events
_です。基本的な考え方は、オブジェクトをハッシュとして使用して、登録されたコールバックを追跡することです。
個人的に私はこれを使用します: エミッタ 。
on()
、off()
、およびemit()
などの甘い短いメソッド名を使用した、非常にシンプルでエレガントなソリューションです。 new Emitter()
を使用して新しいインスタンスを作成するか、Emitter(obj)
を使用してイベント機能を既存のオブジェクトに混在させることができます。このライブラリはCommonJSモジュールシステムで使用するために記述されていますが、_module.exports = ...
_行を削除することで他の場所で使用できます。
真のイベント機能(バブル、stopPropagationなど)が必要ない場合は、独自のイベントを実装できます。 addEventListenerはDOMの単なるAPIであるため、DOMの外部にある独自のオブジェクトには本当に必要ありません。オブジェクトの周りにイベント化されたパターンを作成したい場合、追加のブラウザAPIを必要とせず、非常に下位互換性のある、それを行う良い方法があります。
ディスパッチメソッドが呼び出されたときに多数のイベントをトリガーするオブジェクトがあるとします。
var OurDispatcher, dispatcher;
OurDispatcher = (function() {
function OurDispatcher() {
this.dispatchHandlers = [];
}
OurDispatcher.prototype.on = function(eventName, handler) {
switch (eventName) {
case "dispatch":
return this.dispatchHandlers.Push(handler);
case "somethingElse":
return alert('write something for this event :)');
}
};
OurDispatcher.prototype.dispatch = function() {
var handler, i, len, ref;
ref = this.dispatchHandlers;
for (i = 0, len = ref.length; i < len; i++) {
handler = ref[i];
setTimeout(handler, 0);
}
};
return OurDispatcher;
})();
dispatcher = new OurDispatcher();
dispatcher.on("dispatch", function() {
return document.body.innerHTML += "DISPATCHED</br>";
});
dispatcher.on("dispatch", function() {
return document.body.innerHTML += "DISPATCHED AGAIN</br>";
});
dispatcher.dispatch();
たいていの場合、それ以上に複雑である必要はありません。これにより、イベントを適切に制御でき、すべてが広くサポートされているため、後方互換性や外部ライブラリについて心配する必要はありません。技術的には、setTimeoutなしでも実行でき、APIなしでコールバックを処理できます。 stopPropagation()のような他のものはすべて自分で処理する必要があります。
https://jsfiddle.net/ozsywxer/
もちろん、CustomEventにはポリフィルがありますが、高度なイベント機能が必要でない限り、自分のイベントシステムを「クラス」にラップし、それで他のクラス/関数を拡張することを好みます。
以下にCoffeeScriptバージョンを示します。これはJavaScriptの派生元です。 https://jsfiddle.net/vmkkbbxq/1/
^^わかりやすくなりました。
Javascriptオブジェクトをリッスンする場合、次の3つの方法があります。
Object get/set operators
、 Object.defineProperty
、 Object.prototype.watch
または Proxy API
Object.observe
を使用します。 Works Chrome 25+(2014年1月)。しかし、2016年に 非推奨 になりましたsup/pubパターンについて:
イベントを公開する必要があります。
ネイティブ実装について:
Object get/set operators
は、追加、削除、変更、イベントの取得をリッスンするのに十分です。演算子には support があります。 IE8-のみの問題。ただし、IE8でget/setを使用する場合は、Object.defineProperty
を使用しますが、DOMオブジェクトではObject.defineProperty sham を使用します。Object.prototype.watch
には優れたES5があります polyfill 。Proxy API
にはES Harmonyサポートが必要です。Object.observeの例
var o = {};
Object.observe(o, function (changes) {
changes.forEach(function (change) {
// change.object contains changed object version
console.log('property:', change.name, 'type:', change.type);
});
});
o.x = 1 // property: x type: add
o.x = 2 // property: x type: update
delete o.x // property: x type: delete
2つの問題があります。
まず、iSubmit.addEventListener()
メソッドは、実際にはEventTarget
DOMインターフェースのメソッドです。
これらは、DOM要素でのみ使用するためのものです。メソッドとしてiSubmit
オブジェクトに追加することにより、EventTarget
ではないオブジェクトで呼び出します。 Chrome=が_Uncaught TypeError: Illegal invocation
_JavaScriptエラーをスローする理由です。
最初の問題は重大ですが、EventTarget#addEventListener()
を使用できた場合、イベントはiSubmit
に追加されますが、document
からディスパッチされるため、コードは機能しません。通常、イベントリスナーをアタッチしてイベントをディスパッチするときは、同じオブジェクトのメソッドを使用する必要があります( 異なるストーリー であるバブリングイベントを使用している場合を除きます):バブリングはJavaScriptまたはDOM関連に限定されませんイベント、 たとえば )。
独自のオブジェクトでカスタムイベントを使用するのはごく普通のことです。 Evan Yuの言及 のように、これにはライブラリがあります。ここにカップルがあります:
_js-signals
_を使用しましたが、かなり気に入っています。 _Wolfy87/EventEmitter
_を使用したことはありませんが、見た目はいいです。
_js-signals
_を使用した場合、例は次のようになります。
_var iSubmit = {
finished: new signals.Signal(),
test: function test(memo) {
this.finished.dispatch(memo || {});
}
};
iSubmit.finished.add(function(data) {
console.log('finished:', data);
});
iSubmit.test('this is the finished data');
// alternatively
iSubmit.finished.dispatch('this is dispatched directly from the signal');
_
Node.js環境にいる場合、 NodeのEventEmitterクラス を使用できます。
CustomObject.js
const EventEmitter = require('events');
class CustomObject extends EventEmitter {
constructor() {
super();
}
doSomething() {
const event = {message: 'Hello World!'};
this.emit('myEventName', event);
}
}
module.exports = CustomObject;
使用法:
const CustomObject = require('./CustomObject');
// 1. Create a new instance
const myObject = new CustomObject();
// 2. Subscribe to events with ID "myEventName"
myObject.on('myEventName', function(event) {
console.log('Received event', event);
});
// 3. Trigger the event emitter
myObject.doSomething();
Node.js環境外でNodeのEventEmitterを使用する場合は、 webpack (できればv2.2以降)を使用してCustomClass
のバンドルを取得し、 EventEmitterポリフィル(webpackにより構築)。
仕組みは次のとおりです(npm install -g webpack
を使用してwebpackをグローバルにインストールしたと仮定):
webpack CustomObject.js bundle.js --output-library=CustomObject
を実行しますbundle.js
を含めます(window.CustomObject
を公開します)index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Title</title>
<script src="bundle.js"></script>
</head>
<body>
<script>
// 1. Create a new instance
const myObject = new window.CustomObject();
// 2. Subscribe to events with ID "myEventName"
myObject.on('myEventName', function(event) {
console.log('Received event', event);
});
// 3. Trigger the event emitter
myObject.doSomething();
</script>
</body>
</html>
単なる憶測です。私は自分で試したことはありません。ただし、ダミー要素を作成し、ダミー要素のイベントを発火/聞くことができます。また、私はライブラリなしで行くことを好みます。
function myObject(){
//create "dummy" element
var dummy = document.createElement('dummy');
//method for listening for events
this.on = function(event, func){dummy.addEventListener(event, func);};
//you need a way to fire events
this.fireEvent = function(event, obj){
dummy.dispatchEvent(new CustomEvent(event, {detail: obj}));
}
}
//now you can use the methods in the object constructor
var obj = new myObject();
obj.on("custom", function(e){console.log(e.detail.result)});
obj.fireEvent("custom", {result: "hello world!!!"});
この記事では、カスタムイベントの作成について説明します。 http://www.sitepoint.com/javascript-custom-events/
以下に例を示します。
イベントを作成する-
var event = new CustomEvent(
"newMessage",
{
detail: {
message: "Hello World!",
time: new Date(),
},
bubbles: true,
cancelable: true
}
);
イベントを何かに割り当てる-
document.getElementById("msgbox").dispatchEvent(event);
イベントを購読する-
document.addEventListener("newMessage", newMessageHandler, false);
ブラウザーでNode.jsスタイルの構文を使用してこれを行う方法を次に示します。
Events
クラス:
独自のカスタムクラスに動作を追加するには、Eventsオブジェクトを拡張するだけです(以下の例)。
class Events {
constructor () {
this._callbacks = {}
}
on (key, callback) {
// create an empty array for the event key
if (this._callbacks[key] === undefined) { this._callbacks[key] = [] }
// save the callback in the array for the event key
this._callbacks[key].Push(callback)
}
emit (key, ...params) {
// if the key exists
if (this._callbacks[key] !== undefined) {
// iterate through the callbacks for the event key
for (let i=0; i<this._callbacks[key].length; i++) {
// trigger the callbacks with all provided params
this._callbacks[key][i](...params)
}
}
}
}
// EXAMPLE USAGE
class Thing extends Events {
constructor () {
super()
setInterval(() => {
this.emit('hello', 'world')
}, 1000)
}
}
const thing = new Thing()
thing.on('hello', (data) => {
console.log(`hello ${data}`)
})
Github Gistとこのコードのリンクを次に示します。 https://Gist.github.com/alextaujenis/0dc81cf4d56513657f685a22bf74893d
Object $ Deferredとpromiseを使用できると思います。次のようなことができます。
スタック:アプリケーション内の複数のハンドラーを同じpromiseイベントにバインドします。
var request = $.ajax(url);
request.done(function () {
console.log('Request completed');
});
//アプリケーション内の別の場所
request.done(function (retrievedData) {
$('#contentPlaceholder').html(retrievedData);
});
並列タスク:相互の完了を警告するpromiseを返すように複数のpromiseに依頼します。
$.when(taskOne, taskTwo).done(function () {
console.log('taskOne and taskTwo are finished');
});
順次タスク:タスクを順次実行します。
var step1, step2, url;
url = 'http://fiddle.jshell.net';
step1 = $.ajax(url);
step2 = step1.then(
function (data) {
var def = new $.Deferred();
setTimeout(function () {
console.log('Request completed');
def.resolve();
},2000);
return def.promise();
},
function (err) {
console.log('Step1 failed: Ajax request');
}
);
step2.done(function () {
console.log('Sequence completed')
setTimeout("console.log('end')",1000);
});
ここのソース: http://blog.mediumequalsmessage.com/promise-deferred-objects-in-javascript-pt2-practical-use
使用法: jsfiddle
これは単純なアプローチですが、一部のアプリケーションでは機能する場合があります。
CustomEventTarget.prototype = {
'constructor': CustomEventTarget,
on: function( ev, el ) { this.eventTarget.addEventListener( ev, el ) },
off: function( ev, el ) { this.eventTarget.removeEventListener( ev, el ) },
emit: function( ev ) { this.eventTarget.dispatchEvent( ev ) }
}
function CustomEventTarget() { this.eventTarget = new EventTarget }
簡単な答えを探している人のために。私は このドキュメント にアクセスしましたが、これはほとんどのブラウザがサポートしていないことを知るためだけです。しかし、ページの下部に このGitHubページ へのリンクがあり、これは基本的にObject.watch()とObject.unwatch()が行うことを行い、私にとってはうまくいきます!
変更を監視する方法は次のとおりです
/*
* object.watch polyfill
*
* 2012-04-03
*
* By Eli Grey, http://eligrey.com
* Public Domain.
* NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
* https://Gist.github.com/eligrey/384583
*/
// object.watch
if (!Object.prototype.watch) {
Object.defineProperty(Object.prototype, "watch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop, handler) {
var
oldval = this[prop]
, newval = oldval
, getter = function () {
return newval;
}
, setter = function (val) {
oldval = newval;
return newval = handler.call(this, prop, oldval, val);
}
;
if (delete this[prop]) { // can't watch constants
Object.defineProperty(this, prop, {
get: getter
, set: setter
, enumerable: true
, configurable: true
});
}
}
});
}
// object.unwatch
if (!Object.prototype.unwatch) {
Object.defineProperty(Object.prototype, "unwatch", {
enumerable: false
, configurable: true
, writable: false
, value: function (prop) {
var val = this[prop];
delete this[prop]; // remove accessors
this[prop] = val;
}
});
}
そして、これはあなたのコードでなければなりません:
var object = {
value: null,
changeValue: function(newValue) {
this.value = newValue;
},
onChange: function(callback) {
this.watch('value', function(obj, oldVal, newVal) {
// obj will return the object that received a change
// oldVal is the old value from the object
// newVal is the new value from the object
callback();
console.log("Object "+obj+"'s value got updated from '"+oldValue+"' to '"+newValue+"'");
// object.changeValue("hello world");
// returns "Object object.value's value got updated from 'null' to 'hello world'";
// and if you want the function to stop checking for
// changes you can always unwatch it with:
this.unwatch('value');
// you can retrieve information such as old value, new value
// and the object with the .watch() method, learn more here:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch
})
}
};
または次のように短い:
var object = { user: null };
// add a watch to 'user' value from object
object.watch('user', function() {
// object user value changed
});