I found in an example for OpenLayers this code to register a listener on OpenLayers.Control.SelectFeature
var report = function(e) {
OpenLayers.Console.log(e.type, e.feature.id);
};
var highlightCtrl = new OpenLayers.Control.SelectFeature(vectors, {
hover: true,
highlightOnly: true,
renderIntent: "temporary",
eventListeners: {
beforefeaturehighlighted: report,
featurehighlighted: report,
featureunhighlighted: report
}
});
Now I'm wondering what exactly e is. What type is e and what other attributes besides type and feature doese e have? Where can I find the documentation for this?
you can find other attributes of e by using IE to debug
In your example, the 'report' method is called a "callback method", which gets triggered when the event occurs. If you look at the SelectFeature control highlight method, you'll see that "beforefeaturehighlighted" gets triggered first. Take a look at the arguments of the method:
var cont = this.events.triggerEvent("beforefeaturehighlighted", {
feature : feature
});
First one is the name of the event, second one is the parameters to send to the callback method. So, if you inspect your 'e' variable, as Begisen suggested, you'll see that e.feature is available.
That's what your e variable is.
Related
I'm currently attempting to test some code that uses drag-and-drop. I found some other questions that were kinda related to this, but they were way too specific to help me, or not related enough.
This being a test, I'm struggling on trying to automatically execute code inside a .on('drop',function(e){....} event. The main issue is not that I can't run the code inside, but it's that I can't transfer the dataTransfer property, and I can't seem to fake it because it's read-only. Is there anyway to fake the dataTransfer property or otherwise get around it?
I came up with this JSFiddle that serves as a template of what I'm trying to do: https://jsfiddle.net/gnq50hsp/53/
Essentially if you are able to explain to me (if this is at all possible) how I can possibly fake the dataTransfer property, I should be all set.
Side notes:
I'm totally open to other ways of somehow getting inside that code, like for example, maybe its possible to trigger the event and pass in a fake event object with a fake dataTransfer object.
To see the drag-drop behavior, change the JavaScript load type from no-wrap head to on-Load, then you should see what I'm trying to simulate.
Important to note that I cannot modify any of the code inside the event handlers, only inside the outside function
Using Karma/Jasmine so use of those tools are also possible like spies
Also, I'm using Chrome.
Thanks in advance, and let me know for any questions/clarifications!
You should be able to override pretty much everything you want using Object.defineProperty. Depending on what you want to test it can be very simple or very complex. Faking the dataTransfer can be a bit tricky, since there's a lot of restrictions and behaviors linked to it, but if you simply want to test the drop function, it's fairly easy.
Here's a way, this should give you some ideas as to how to fake some events and data:
//Event stuff
var target = $('#target');
var test = $('#test');
test.on('dragstart', function(e) {
e.originalEvent.dataTransfer.setData("text/plain", "test");
});
target.on('dragover', function(e) {
//e.dataTransfer.setData('test');
e.preventDefault();
e.stopPropagation();
});
target.on('dragenter', function(e) {
e.preventDefault();
e.stopPropagation();
});
//What I want to simulate:
target.on('drop', function(e) {
console.log(e)
//Issue is that I can't properly override the dataTransfer property, since its read-only
document.getElementById('dataTransferDisplay').innerHTML = e.originalEvent.dataTransfer.getData("text");
});
function simulateDrop() {
// You'll need the original event
var fakeOriginalEvent = new DragEvent('drop');
// Using defineProperty you can override dataTransfer property.
// The original property works with a getter and a setter,
// so assigning it won't work. You need Object.defineProperty.
Object.defineProperty(fakeOriginalEvent.constructor.prototype, 'dataTransfer', {
value: {}
});
// Once dataTransfer is overridden, you can define getData.
fakeOriginalEvent.dataTransfer.getData = function() {
return 'test'
};
// TO have the same behavior, you need a jquery Event with an original event
var fakeJqueryEvent = $.Event('drop', {
originalEvent: fakeOriginalEvent
});
target.trigger(fakeJqueryEvent)
}
https://jsfiddle.net/0tbp4wmk/1/
As per jsfiddel link you want to achieve drag and drop feature. jQuery Draggable UI already provides this feature why you can not use that?
For create custom event on your way you have to follow two alternative ways
$('your selector').on( "myCustomEvent", {
foo: "bar"
}, function( event, arg1, arg2 ) {
console.log( event.data.foo ); // "bar"
console.log( arg1 ); // "bim"
console.log( arg2 ); // "baz"
});
$( document ).trigger( "myCustomEvent", [ "bim", "baz" ] );
On above example
In the world of custom events, there are two important jQuery methods: .on() and .trigger(). In the Events chapter, we saw how to use these methods for working with user events; for this chapter, it's important to remember two things:
.on() method takes an event type and an event handling function as arguments. Optionally, it can also receive event-related data as its second argument, pushing the event handling function to the third argument. Any data that is passed will be available to the event handling function in the data property of the event object. The event handling function always receives the event object as its first argument.
.trigger() method takes an event type as its argument. Optionally, it can also take an array of values. These values will be passed to the event handling function as arguments after the event object.
Here is an example of the usage of .on() and .trigger() that uses custom data in both cases:
OR
jQuery.event.special.multiclick = {
delegateType: "click",
bindType: "click",
handle: function( event ) {
var handleObj = event.handleObj;
var targetData = jQuery.data( event.target );
var ret = null;
// If a multiple of the click count, run the handler
targetData.clicks = ( targetData.clicks || 0 ) + 1;
if ( targetData.clicks % event.data.clicks === 0 ) {
event.type = handleObj.origType;
ret = handleObj.handler.apply( this, arguments );
event.type = handleObj.type;
return ret;
}
}
};
// Sample usage
$( "p" ).on( "multiclick", {
clicks: 3
}, function( event ) {
alert( "clicked 3 times" );
});
On above example
This multiclick special event maps itself into a standard click event, but uses a handle hook so that it can monitor the event and only deliver it when the user clicks on the element a multiple of the number of times specified during event binding.
The hook stores the current click count in the data object, so multiclick handlers on different elements don't interfere with each other. It changes the event type to the original multiclick type before calling the handler and restores it to the mapped "click" type before returning:
I am using the native DOM's addEventListener event like so:
const domElement = document.getElementById('ember464');
domElement.addEventListener('click', dosomething);
this._listeners.push({target: domElement, type: 'click', callback: dosomething});
this registered the event and stores it in a _listeners array so it can be cleaned up later. The good news is that this event does attach to the DOM but event though the target is clearly scoped to just the single button, the handler seems to fire whenever a click is detected anywhere on the document!
Am I doing something wrong?
p.s. here's the browser inspector looking at the _listener array ... note that highlighting the "target" does indeed point only to the button that I'm trying to target. AKA, the target is the DOM node, not the selector used to identify the node.
I've now looked at the debugger at the targeted DOM element and I see the following:
For anyone brave enough, here's a working demonstration of the code. The second button is triggered by the "click" event is the subject of this issue:
https://ui-animate.firebaseapp.com/
The full source code is here: link
Ok, the problem was in a bit of code I hadn't illustrated in the question. Modern browsers all support the DOM method addEventListener but in order to support older browsers who only have attachEvent I had added the following abstraction:
registerListener(target, type, callback) {
const addListener = target.addEventListener || target.attachEvent;
const eventName = target.addEventListener ? type : 'on' + type;
addListener(eventName, callback);
},
The problem with this is that the "this" context is lost in the call. So instead I changed to the following:
registerListener(target, type, callback) {
const listenerFunction = target.addEventListener ? 'addEventListener' : 'attachEvent';
const eventName = target.addEventListener ? type : 'on' + type;
target[listenerFunction](eventName, callback);
},
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()
I'm the using JWPlayer. After setup the player I need to add listeners to some events, to give an example I listen to events.JWPLAYER_MEDIA_TIME like so:
jwplayer('video-container').onTime(this.onTimeHandler);
After a while I need to remove this event listener, reading the documentation I couldn't find any solution.
Looking at the code, it doesn't seem possible to remove an event listener: a callback is pushed onto an array when you call onTime (or any of the other methods to setup event handlers), so calling it a second time doesn't overwrite a previous listener but just adds a new listener to the array.
Perhaps an alternative could be to set a flag once your listener doesn't have to perform its task anymore:
onTimeHandler : function() {
if (! this.handleOnTimeEvents)
return;
...
}
Here is how I handled it.
create a pseudo function whose sole purpose is to be a pointer. I was concerned with the onComplete event, so I wrote the code like so below:
function createJWPlayer(surl, stitle, autos, pw, ph) {
jwplayer("videocontainer").setup({
file: surl,
title: stitle,
width: pw,
height: ph,
autostart: autos,
stretching: "uniform",
skin: "/Scripts/JWPlayer/six.xml"
});
jwplayer().onComplete(function (e) {
jwcompleteevent(e);
});
}
function jwcompleteevent(e) {
// method to remain empty, sole purpose is to provide a pointer for the handler
}
Then in the function where I created it, I wrote this:
var mcomplete = (selobj.HasQ == false) ? InterActNoTimeAutoS : jwpCompleteInterA;
createJWPlayer(selobj.Upath, ti.TestTitle, true, "100%", "100%");
jwcompleteevent = mcomplete;
If I needed to load another video, I would do this
mcomplete = (selobj.HasQ == false) ? InterActNoTimeAutoS : jwpCompleteInterA;
jwcompleteevent = mcomplete;
loadJwPlayerUrl(selobj.Upath, true);
If anyone sees a problem with this, please tell me, it seems to be working as needed in the development environment
$(document).ready(function(){
var _new_li = $('', {
'id': 'p',
'text': 'CLICKME',
click: function(){
alert('fired');
},
data: {
'somedata': 'somedata',
}
});
_new_li.appendTo($("#example"));
});
I receive an "Uncaught TypeError: Cannot read property 'click' of undefined", when I try to click the element which I created like so.
But, if you switch click: and data: it works.
$(document).ready(function(){
var _new_li = $('<li/>', {
'id': 'p',
'text': 'CLICKME',
data: {
'somedata': 'somedata',
},
click: function(){
alert('fired');
}
});
_new_li.appendTo($("#example"));
});
any explanation for that behavior?
Kind Regards
--Andy
PS:
I posted a similar behavior earlier in the jQuery Core Development forum, Mr. Swedberg answered there:
I'm pretty sure this is happening because you're setting data with an object, which >(until 1.4.2) would overwrite the event object. Not sure which version of jQuery you're >using in your project, but it looked like the jsbin example was using 1.4. Try upgrading >to 1.4.2 and see if that helps.
But it seems like the problem still exists in 1.4.2
Post rewrited.
You shouldn't put that comma after last (and only) element in data.
After trying some stuff I got to this:
$(document).ready(function(){
var fun=function(){
alert('fired');
};
var parms={
'id': 'p',
'text': 'CLICKME',
'click':fun,
'data': {
'somedata': 'somedata'
}
};
console.log(parms);
var _new_li = $('<li/>',parms);
_new_li.appendTo($("#example"));
});
Everything works fine until I click on the li element. Then I get e is undefined (jquery line 55). Works well when click and data are swapped.
Still investigating
AND FOUND IT
jquery development version, line 1919
var events = jQuery.data(this,
"events"), handlers = events[
event.type ];
events is undefined.
jquery overwrites events stored in data.
so this IS a bug. It should just extend.
I've submited a bug report.
folks.. this is no bug.. what's happening is that you need to use the event data argument
.click( [ eventData ], handler(eventObject) )
eventDataA map of data that will be passed to the event handler.
to know why that's necessary you can review the discussion in the jquery documentation of .bind().. you may also want to review the concept of "closure" in javascript.. it provides the rationale behind this whole thing
this part is especially relevant (taken from http://api.jquery.com/bind/):
Passing Event Data
The optional eventData parameter is not commonly used. When provided, this argument allows us to pass additional information to the handler. One handy use of this parameter is to work around issues caused by closures. For example, suppose we have two event handlers that both refer to the same external variable:
var message = 'Spoon!';
$('#foo').bind('click', function() {
alert(message);
});
message = 'Not in the face!';
$('#bar').bind('click', function() {
alert(message);
});
Because the handlers are closures that both have message in their environment, both will display the message Not in the face! when triggered. The variable's value has changed. To sidestep this, we can pass the message in using eventData:
var message = 'Spoon!';
$('#foo').bind('click', {msg: message}, function(event) {
alert(event.data.msg);
});
message = 'Not in the face!';
$('#bar').bind('click', {msg: message}, function(event) {
alert(event.data.msg);
});
This time the variable is not referred to directly within the handlers; instead, the variable is passed in by value through eventData, which fixes the value at the time the event is bound. The first handler will now display Spoon! while the second will alert Not in the face!
I ended up modifying the source code slightly, so it would not randomly blow up here.
handle: function( event ) {
...
// tbn modified source
try {
var events = jQuery.data(this, "events"), handlers = events[ event.type ];
} catch(e) {}
// tbn modified source
..then re-minified the source
I have not had any problems with this since (yet).
Not sure if this helps, but I was running into a similar issue. I was trying to bind events like mouseout, click, etc... to an new LI element. I kept getting the error 'Uncaught TypeError: Cannot read property 'mouseout' of undefined'. Turns out, you have to bind events like click, mouseout, etc... AFTER you've appended the element to the DOM. Not before. Rough example:
newLI = $('<li />');
$(document.body).append(newLI); // Correct
newLI.click(...);
newLI.mouseout(...);
instead of
newLI = $('<li />');
newLI.click(...);
newLI.mouseout(...);
$(document.body).append(newLI); // Incorrect