What i'm trying to do is a combination of a mootools class and raphael. The problem i got is mainly mootools event binding i guess.
I'm trying to append an event to a raphael element (dom node) and when firing the event another class method should be called.
This is no problem when coding without a mootools class. But this (the right) way i have some problems. When binding the events, the raphael element cannot be longer used because "this" now refers to the mootools class.
Please take a look at this code and i guess you will understand what my problem is:
// mootools class
var test = new Class({
...
initPlane: function() {
// just an JSON object array
this.objects = [{"pid":"2","sx":"685","sy":"498","dx":"190","dy":"540"},{"pid":"3","sx":"156","sy":"341","dx":"691","dy":"500"}];
// place the objects on stage and append some events to them
this.objects.each(function(item, idx){
item.gfx = this.gfx.image("assets/img/enemy.png", item.sx, item.sy, 32, 32);
// #### differnt approaches to bind the events. all not working
// first attempt with mootools event
item.gfx.node.addEvent('click', function(e) {
console.log(this.attr('x')); // not working because this is bound to the class i guess
this.info();
}.bind(this));
// second attempt with mootools event
item.gfx.node.addEvent('click', function(e) {
console.log(this.attr('x')); // not working
parent.info(this); // no binding and not working
});
// first attempt with raphael event
item.gfx.click( function(e) {
console.log(this.attr('x')); // works !
this.info(this); // not working because this refers to raphael element.
});
}.bind(this))
},
// this method should be called after click event and output element attribs
info: function(event) {
console.log(event.attr('x'));
},
...
});
your .each is wrong.
Object.each(obj, function(el, key, obj) {
}, bind);
http://mootools.net/docs/core/Types/Object#Object:Object-each
although you actually have this.objects as array, did not notice :)
Array.each(function(el, index) {
}, bind);
when you need this to be bound to element on click, that's fine. just store a copy of this into self and call self.info() instead. alternatively, keep the bind and reference e.target as the trigger element instead, whilst this is your instance
although it may seem 'neater' to try to keep this bound to the class wherever possible, mootools-core devs tend to prefer the var self = this; way as it avoids the extra callback to bind etc (look at the mootools source, very common)
also, say you want to have the click event go to a method directly:
element.addEvent("click", this.info.bind(this));
which will send the event as the 1st argument to info (so reference event.target).
bind can usually apply arguments as well as the scope (depending on the implementation), and that allows you to write function (raphaelObj, node) { ... }.bind(null, this, item.gfx.node) to bind two arguments.
Related
Using jQuery I need to:
persists list of all event handlers that are added to element,
remove them all for few seconds and
return things to initial state (reassign the same event handlers)
I found that get list of current listeners with (some jQuery inner mechanisms):
var eventsSubmitBtn = $._data(submitButton[0], "events");
Then I can remove all event listeners with
submitButton.off();
But last stem seems not to be working
setTimeout(function () {
$._data(submitButton[0], "events", eventsSubmitBtn);
}, 5000);
eventsSubmitBtn is an empty array.
Is this the way this should be done with initial setting and I'm need something like deep cloning for those objects or this can't be done with $._data?
N.B. I have possibility to add my cistom code after all other system js code, thus I can't place the code assigning to $.fn.on before anything. Code that I write will run the last on startup and other event listeners are attached before my scripts will run.
As you get a reference to the object returned by $._data(), any change to that object will not go unnoticed, i.e. after you invoke .off(), that object will have changed to reflect that there are no handlers attached any more.
You could solve this by taking a shallow copy of the object, (e.g. with Object.assign).
But this is not really a recommended way to proceed. According to a jQuery blog, "jQuery._data(element, "events") ... is an internal data structure that is undocumented and should not be modified.". As you are modifying it when restoring the handlers, this cannot be regarded best practice. But even only reading it should only be used for debugging, not production code.
It would be more prudent to put a condition in your event handling code:
var ignoreEventsFor = $(); // empty list
$("#button").on('click', function () {
if (ignoreEventsFor.is(this)) return;
// ...
});
Then, at the time it is needed, set ignoreEventsFor to the element(s) you want to ignore events for. And when you want to revert back to normal, set it to $() again.
Now adding this to all your event handlers may become a burden. If you stick to using on() for attaching event handlers, then you could instead extend $.fn.on so it will add this logic to the handlers you pass to it.
The following demo has a button which will respond to a click by changing the background color. With a checkbox you can disable this from happening:
/* Place this part immediately after jQuery is loaded, but before any
other library is included
*/
var ignoreEventsFor = $(), // empty list
originalOn = $.fn.on;
$.fn.on = function (...args) {
var f = args[args.length-1];
if (typeof f === 'function') {
args[args.length-1] = function (...args2) {
if (ignoreEventsFor.is(this)) return;
f.call(this, ...args2);
};
}
originalOn.call(this, ...args);
}
/* This next part belongs to the demo, and can be placed anywhere */
$(function () {
$("#colorButton").on('click', function () {
// Just some handler that changes the background
var random = ('00' + (Math.random() * 16*16*16).toString(16)).substr(-3);
$('body').css({ backgroundColor: "#" + random });
});
$("#toggler").on('change', function () {
// Toggle the further handling of events for the color button:
ignoreEventsFor = $(this).is(':checked') ? $("#colorButton") : $();
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="colorButton">Change color</button><br>
<input type="checkbox" id="toggler">Disable events
Notice: the above code uses ES6 spread/rest syntax: if you need support for IE then that would have to be written using the arguments variable, apply, ...etc.
This is my js file content:
window.onload = function() {
obj = document.getElementById("_accountwebsite_id");
Event.observe(obj, 'change', function () {
alert('hi');
});
}
I want to fire the on change event for my dropdown: _accountwebsite_id . The prototype library it is loaded before this file. I got no errors in the console. Where am i wrong ? thx
You're doing a lot of extra work here that Prototype does for you. First off, setting the document's onload method not only is really old-school, it also will clobber any previously set observer on that event.
$(document).observe('dom:loaded', function( ... ){...});
...is the modern way to register one (or more) event listeners to the document load event.
Next, you're using getElementById here, which will work, but does not return a Prototype-extended object in some browsers.
$('element-id');
...will both get the element reference and extend it if your browser failed to respect every aspect of prototypal inheritance.
Finally, this whole thing can be made both simpler and more bulletproof by using a deferred observer. Imagine if your interface DOM was updated by Ajax -- that would make your observer miss the events fired by this select element, because it was not referring to the same (===) element, even if the ID matched.
$(document).on('change', '#_accountwebsite_id', function(evt, elm){
alert(elm.inspect());
});
This observer will respond to any change event on an element with the correct ID, even if it was added after the observer was registered with the document.
I have a setup theoretically like this [see fiddle -> http://jsfiddle.net/GeZyw/] :
var EventTest = function(element) {
this.element = element;
this.element.addEventListener('click', elementClick);
function elementClick() {
var event = document.createEvent('CustomEvent');
event.initEvent('myevent', false, false);
event['xyz']='abc';
event.customData='test';
console.log(event);
this.dispatchEvent(event);
}
}
var element = document.getElementById('test');
var test = new EventTest(element);
$(document).ready(function() {
$("#test").on('myevent', function(e) {
console.log('myevent', e);
});
});
What I want is to create a CustomEvent in pure Javascript, enrich it with some properties and trigger that event so it can be cached also by a library like jQuery.
As you can see in the fiddle, the CustomEvent is triggered well and it is actually populated with custom properties - but when it reaches jQuery on() the custom properties is gone from the first level. My custom properties is now demoted to e.originalEvent.xyz and so on.
That is not very satisfactory. I want at least my own properties to be at the first level.
Also, in a perfect world, I would like to get rid of most of the standard properties in the dispatched event, so it contained (theoretically optimal) :
e = {
xyz : 'abc',
customData : 'test'
}
Is that possible at all? If so, how should I do it?
I have run into the same issue, couple of months ago, the point is:
When an event is received by jQuery, it normalizes the event properties before it dispatches the event to registered event handlers.
and also:
Event handlers won't be receiving the original event. Instead they are getting a new jQuery.Event object with properties copied from the raw HTML event.
Why jQuery does that:
because it can't set properties on a raw HTML event.
I had decided to do the same, I started to do it with a nasty way, and my code ended up so messy, at the end I decided to use jQuery.trigger solution, and pass my event object as the second param, like:
$("#test").bind("myevent", function(e, myeventobj) {
alert(myeventobj.xyz);
});
var myobj = {"xyz":"abc"};
$("#test").trigger("myevent", myobj);
for more info check this link out: .trigger()
Given a generic handler bound to a series of links with YUI, how do I find out which link triggered the event?
YUI().use('node', function (Y) {
var list = Y.one('#studentList'), links;
links = list.all('a');
links.on('click', function (e) {
alert(this.get('id')); // this just shows a comma delimited list of all ids
});
});
I suppose I could bind each link individually instead of using the "on" idiom on the links list, but it seems odd to me that YUI would not provide access to the DOM node. Digging into the event object shows several private fields that look like the DOM node, but surely there must be a safe way of doing this.
e.currentTarget appears to be what you're looking for:
links.on('click', function (e) {
alert(e.currentTarget.get('id'));
});
From NodeList's on:
By default, the this object will be the NodeList that the subscription came from, not the Node that received the event. Use e.currentTarget to refer to the Node.
In my JavaScript and Flex applications, users often perform actions that I want other JavaScript code on the page to listen for. For example, if someone adds a friend. I want my JavaScript app to then call something like triggerEvent("addedFriend", name);. Then any other code that was listening for the "addedFriend" event will get called along with the name.
Is there a built-in JavaScript mechanism for handling events? I'm ok with using jQuery for this too and I know jQuery makes extensive use of events. But with jQuery, it seems that its event mechanism is all based around elements. As I understand, you have to tie a custom event to an element. I guess I can do that to a dummy element, but my need has nothing to do with DOM elements on a webpage.
Should I just implement this event mechanism myself?
You have a few options:
jQuery does allow you to do this with objects not associated with the document. An example is provided below.
If you're not already using jQuery on your page, then adding it is probably overkill. There are other libraries designed for this. The pattern you are referring to is called PubSub or Publish/Subscribe.
Implement it yourself, as you've suggested, since this is not difficult if you're looking only for basic functionality.
jQuery example:
var a = {};
jQuery(a).bind("change", function () {
alert("I changed!");
});
jQuery(a).trigger("change");
I would implement such using MVVM pattern with knockjs library.
Just create an element, and use jquery events on it.
It can be just a global variable, doesn't even have to be connected to the DOM.
That way you accomplish your task easily and without any extra libs.
Isn't it possible to bind onchange events in addition to click events? For instance, if addFriend is called and modifies a list on the page, you could bind the change event to then invoke additional functionality.
$('#addFriendButton').click( function() {
// modify the #friendList list
});
$('#friendList').change( function() {
myOtherAction();
});
This is total Host independent, no need for jQuery or dom in this case!
function CustomEvents(){
//object holding eventhandlers
this.handlers_ = {};
}
//check if the event type does not exist, create it.
//then push new callback in array.
CustomEvents.prototype.addEventListner = function (type, callBack){
if (!this.handlers_[type]) this.handlers_[type] = [];
this.handlers_[type].push(callBack);
}
CustomEvents.prototype.triggerEvent = function (type){
//trigger all handlers attached to events
if (!this.handlers_[type]) return;
for (var i=0, handler; handler = this.handlers_[type][i]; i++)
{
//call handler function and supply all the original arguments of this function
//minus the first argument which is the type of the event itself
if (typeof handler === "function") handler.apply(this,arguments.slice(1));
}
}
//delete all handlers to an event
CustomEvents.prototype.purgeEventType = function(type){
return delete this.handlers_[type];
}
test:
var customEvents = new CustomEvents();
customEvents.addEventListner("event A", function(arg){alert('Event A with arguments' + arg);));
customEvents.triggerEvent("event A", "the args");
EDIT added arguments passing