I'm having problems to retrieve the argument passed to my eventHandler.
I'm using geous to put a map inside and activeadmin interface. The solution is working on the show page but I didn't get things working on the index page. The idea is to attach the handler to the dragend event on a marker with my model geographic coordinates.
What I do:
map.locations.add($fields.geousable('getLocation'), { draggable: true, on: { dragend: setFieldsLocation }});
and setFieldsLocation is defined below:
function setFieldsLocation (event) {
alert(setFieldsLocation.caller);
console.log(event);
$('.geousable').find("input")[0].value = event.data.lat;
$('.geousable').find("input")[1].value = event.data.lng;
};
So the first line bind the handler (for dragend) and a method inside the geous lib code attachs it and calls it when the event is fired, here is the snippet:
var _onAdd = function (locationMarker, opts) {
// not relevant code
if (options.on) {
for (event in options.on) {
eventHandler = function() {
var handler = options.on[event],
location = locationMarker.location;
return function() {
handler.call(location);
}
}();
google.maps.event.addListener(locationMarker.marker, event, eventHandler);
}
}
};
Debugger shows the location correct location variable when the handler is called, but I can't get the location variable inside my "setFieldsLocation" function. I tried a lot of things. I changed the header of the javascript function, I used event, e, arguments and this kind of things:
console.log("arguments.callee.caller.name = " +
arguments.callee.caller.name);
console.log("arguments.callee.caller.toString() = " +
arguments.callee.caller.toString());
But no luck.
Ideas with an explanation of what is occurring here will be very appreciated.
Related
I have an object :
var myObject = {
open : function() {
console.log('Object open');
$(this).trigger('open');
}
};
$(myObject).on('open', function() {
console.log('Open event received');
});
myObject.open();
This code throw an "too much recursion".
The problem is that trigger is calling the method, if I rename the open method, this works :
var myObject = {
_open : function() {
console.log('Object open');
$(this).trigger('open');
}
};
$(myObject).on('open', function() {
console.log('Open event received');
});
myObject._open();
Does this make sense for anyone ?
Well you created a recursive loop when you try to trigger 'open' again.
a reccursive loop is a function that calls itself over and over agains until the "stackoverflow" error happen ( no puns intended). it's a more efficient way to make loops.
on the second scenario, you trigger _open and then the function tries to trigger the 'open' event, which trigger the on('open') listener.
simple as that.
In your first example
$(this).trigger('open');
recursively triggers the invocation of the myObject.open function.
In a second one
$(this).trigger('open');
triggers new custom event 'open' and then you handle this event with handler that you defined via on() method.
So it sounds like your trying to create a event listener for when your function is executed?
If so this is how I have handled many of these types of events. I use custom events bound to the document so they are avaiable at a global level.
JS Fiddle: https://jsfiddle.net/7eaoe1hp/
var myObject = {
open : function() {
console.log('Object open');
$(document).triggerHandler('open');
}
};
$(document).on('open', function(){
console.log('Open event received');
});
myObject.open();
I need to fire a custom event each time when clicked on div with different data attached.
Here is a simplified variant of my code (JSFiddle):
<div onclick="selectItem(Math.random())">click me</div>
<script>
function selectItem(id) {
var event_data = {
myid: id
};
if (!arguments.callee.event)
arguments.callee.event = new CustomEvent("selectItem", {detail: event_data});
arguments.callee.event.detail = event_data; // no success here
document.dispatchEvent(arguments.callee.event);
}
document.addEventListener("selectItem", function(event) {
console.log(event.detail); // same thing all the time :(
});
</script>
But in the event listener function I receive the same data each time the event is fired. I tried to change the event before dispatchEvent but seems it is read only object.
Is there any other options to send different data each time i click on div?
The reason is that detail property of the event can be any object but they are read only, i.e they can be set only when the event is created.Iit is specifically used to provide details regarding the event, and not for attaching data for each dispatch of the event.
interface CustomEvent {
readonly attribute any detail;
};
Probably you can just set a custom property data to the event during each dispatch and access that property.
Try:
function selectItem(id) {
var event_data = {
myid: id
};
if (!arguments.callee.event) arguments.callee.event = new CustomEvent("selectItem");;
arguments.callee.event.data = event_data;
document.dispatchEvent(arguments.callee.event);
}
document.addEventListener("selectItem", function(event) {
console.log(event.data);
});
Fiddle
Or you would need to init the custom Event each time to set the details property like this:
arguments.callee.event.initCustomEvent("selectItem", true, true, event_data);
and details property will have new updated value each time the event is dispacthed.
Demo
The issue is i am following the tutorial here and the functionality of the new widget work fine until i hover over the widget where a this._changeBackground method is being called from "on" listeners, i get the error TypeError: this._changeBackground is not a function
The final code as implemented from tutorial looks like this:
define(["dojo/_base/declare","dijit/_WidgetBase", "dijit/_TemplatedMixin", "dojo/text!/JS/Allatus/Test.html", "dojo/dom-style", "dojo/_base/fx", "dojo/_base/lang","dojo/on"],
function(declare, WidgetBase, TemplatedMixin, template, domStyle, baseFx, lang , on){
return declare([WidgetBase, TemplatedMixin], {
// Some default values for our author
// These typically map to whatever you're handing into the constructor
name: "No Name",
// Using require.toUrl, we can get a path to our AuthorWidget's space
// and we want to have a default avatar, just in case
avatar: require.toUrl("JS/Allatus/custom/android_vector.jpg"),
bio: "",
// Our template - important!
templateString: template,
// A class to be applied to the root node in our template
baseClass: "authorWidget",
// A reference to our background animation
mouseAnim: null,
// Colors for our background animation
baseBackgroundColor: "#fff",
mouseBackgroundColor: "#def",
postCreate: function(){
// Get a DOM node reference for the root of our widget
var domNode = this.domNode;
// Run any parent postCreate processes - can be done at any point
this.inherited(arguments);
// Set our DOM node's background color to white -
// smoothes out the mouseenter/leave event animations
domStyle.set(domNode, "backgroundColor", this.baseBackgroundColor);
// Set up our mouseenter/leave events - using dojo/on
// means that our callback will execute with `this` set to our widget
on(domNode, "mouseenter", function (e) {
this._changeBackground(this.mouseBackgroundColor);
});
on(domNode, "mouseleave", function (e) {
this._changeBackground(this.baseBackgroundColor);
});
},
_changeBackground: function(toCol) {
// If we have an animation, stop it
if (this.mouseAnim) { this.mouseAnim.stop(); }
// Set up the new animation
this.mouseAnim = baseFx.animateProperty({
node: this.domNode,
properties: {
backgroundColor: toCol
},
onEnd: lang.hitch(this, function() {
// Clean up our mouseAnim property
this.mouseAnim = null;
})
}).play();
},
_setAvatarAttr: function(av) {
// We only want to set it if it's a non-empty string
if (av != "") {
// Save it on our widget instance - note that
// we're using _set, to support anyone using
// our widget's Watch functionality, to watch values change
this._set("avatar", av);
// Using our avatarNode attach point, set its src value
this.avatarNode.src = av;
}
}
});
});
Any Ideas Why I cant call another function within my customize widget ? is that just a bug or i am doing something wrong?
Your mouseEnter function is being called outside the scope of your widget (scope in JS refers to the value of the "this" variable). This is a common problem and dojo has a simple solution, the function lang.hitch can be used to tie a function to a certain scope. (and more, I would recommending reading the docs on it). Here's how you should use it in this scenario :
// Set up our mouseenter/leave events - using dojo/on
// means that our callback will execute with `this` set to our widget
on(domNode, "mouseenter", lang.hitch(this, function (e) {
this._changeBackground(this.mouseBackgroundColor);
}));
on(domNode, "mouseleave", lang.hitch(this, function (e) {
this._changeBackground(this.baseBackgroundColor);
}));
The scope of this by default in on callbacks is window. Since you want the scope to be the widget itself, you need to import dojo/_base/lang and use the lang#hitch function to explicitly set the scope of the callback
on(domNode, "mouseenter", lang.hitch(this,function (e) {
this._changeBackground(this.mouseBackgroundColor);
}));
on(domNode, "mouseleave", lang.hitch(this,function (e) {
this._changeBackground(this.baseBackgroundColor);
}));
When I fire a function I want it to apply listeners just to elements I pass, particular this jQuery element.
addEventListeners(this);
function addEventListeners(el) {
$(el).mouseenter(function() {
$(this).stop(true,true).switchClass("", "HIGHLIGHT", 400, "easeInOutQuad");
});
$(el).mouseleave(function() {
$(this).stop(true,true).switchClass("HIGHLIGHT", "", 400, "easeInOutQuad");
});
}
It fires from AJAX result:
$.post(url,{el:wartosc_inputa},function(returned) {
var data = $(returned).hide();
$("#listaElementow").prepend(data);
data.slideDown();
loading();
addEventListeners(this);
});
How to code it good? This code is not passing variables from addEventListeners(this); to function.
in the ajax callback function "this" will be the ajax object i think and no longer an element so you need to save "this" in a variable before the ajax starts.
that = this;
$.post(url,{el:wartosc_inputa},function(returned) {
var data = $(returned).hide();
$("#listaElementow").prepend(data);
data.slideDown();
loading();
addEventListeners(that);
});
Judging from the context of the rest of your success handler, I assume returned is the DOM element you're attempting to bind your handlers to. Assuming this is the case:
$.post(url,{el:wartosc_inputa},function(returned) {
var data = $(returned).hide();
$("#listaElementow").prepend(data);
data.slideDown();
loading();
addEventListeners(data[0]);
// change this ^^^^
// pass the DOM element here, or
// change the addEventListeners function to take a jquery element
});
this in that context is not what you expect it to be. Try this:
var self = this;
$.post(url,{el:wartosc_inputa},function(returned) {
var data = $(returned).hide();
$("#listaElementow").prepend(data);
data.slideDown();
loading();
addEventListeners(self);
});
So, this has been bugging me for quite a while.
I have a javascript constructor that goes smth like this:
function TimeBlock(c, local_count){
/* creates a timeblock */
.
.
.
_getel(this.id).addEventListener('mouseover', this.displayPopup)
_getel(this.id).addEventListener('mouseout', this.killPopup)
}
and basically it creates a TimeBlock object and assigns to it those two event handlers, which are defined in TimeBlock.prototype like so:
TimeBlock.prototype = {
.
.
.
block = getTimeBlock(e.target.id)
//do something
},
killPopup: function(e){
// //do something else
}
}
And it works. I call new Timeblock (some_parameters) and i get my timeblock, it creates the element into the html (it's a div) and assigns the listener to it. I test it and it works fine. But as soon as I call it again, I get a new TimeBlock element created, with working listeners, but the previous one stops working. It's like assigning those listeners onto the new element cancels out the listeners that should be on the previous element.
What is even weirder is that I've assigned those listeners manually through a javascript console and it worked fine there. I'm using Firefox 7.0.1 and Firebug for those tests.
Thanks a lot
This code is suspicious:
_getel(this.id).addEventListener('mouseover', this.displayPopup)
...because although you're hooking up the function as an event handler, you're not doing anything to preserve what this means inside the handler. So when the handler is called, this will not be the same as it was in the code above. I suspect this loss of what this means is what's causing the effect you're seeing when you try to do this with more than one TimeBlock instance.
There are various ways to ensure that your functions are called with the correct this value, one of which looks like this:
function TimeBlock(c, local_count){
/* creates a timeblock */
.
.
.
var self = this;
_getel(this.id).addEventListener('mouseover', function(event) {
self.displayPopup(event);
});
_getel(this.id).addEventListener('mouseout', function(event) {
self.killPopup(event);
});
}
More reading:
Mythical methods
You must remember this
The only other thing that comes to mind is that mouseover and mouseout bubble to parent elements, which can be confusing if you're not careful. So for instance, if you have:
<div>Foo <span>bar</span></div>
...and you watch for mouseover and mouseout on the div, you'll see mouseout when the mouse moves from the span ("bar") into the div ("foo"), which can be surprising if you're not expecting it.
Here's an example demonstrating two, independent instances hooked to independent elements as per the above (live copy):
window.onload = function() {
new TimeBlock("firstElement");
new TimeBlock("secondElement");
function _getel(id) {
return document.getElementById(id);
}
function TimeBlock(id){
var self = this;
this.id = id;
this.counter = 0;
_getel(this.id).addEventListener('mouseover', function(event) {
self.displayPopup(event, this);
});
_getel(this.id).addEventListener('mouseout', function(event) {
self.killPopup(event, this);
});
}
TimeBlock.prototype.displayPopup = function(event, element) {
++this.counter;
element.innerHTML = "Display, counter = " + this.counter;
};
TimeBlock.prototype.killPopup = function(event, element) {
element.innerHTML = "Remove, counter = " + this.counter;
};
};