I have applied same custom event to the event object in the backbone with 2 different callbacks.
var ourObject = {};
_.extend(ourObject, Backbone.Events);
function dancing (msg) { console.log("We are dancing. " + msg); }
function jumping (msg) { console.log("We are jumping. " + msg); }
ourObject.on("move", dancing);
ourObject.on("move", jumping);
When I trigger move event using ourObject.trigger("move", "Yeah!");,it will trigger both the callbacks.
How should I prevent triggering second callback from first callback?
You can pass an object containing a flag which acts as an event object as second argument:
function dancing(msg, event) {
console.log(event); // flag: false
event.flag = true;
console.log("We are dancing. " + msg);
}
function jumping(msg, event) {
console.log(event); // flag: true
if(!event.flag) return;
console.log("We are jumping. " + msg);
}
ourObject.trigger("move", 'test', {
flag: false
});
Or you can simply do something like:
function dancing (msg) {
this.dancingTriggered = true;
console.log("We are dancing. " + msg);
}
function jumping (msg) {
if(this.dancingTriggered){
this.dancingTriggered = false;
return;
}
console.log("We are jumping. " + msg);
}
Or just use simple variable available in the scope of these callbacks
Related
This function is needed for some time. But when tasks are completed I need to disable it, that after clicking on 'board' it wouldn't work.
board.addEventListener('click', event => {
if (event.target.classList.contains('circle')) {
score++
event.target.remove()
createRandomCircle()
} else if (!event.target.classList.contains('circle')) {
fault++
}
})
//Here I want to deactivate this event listener
I would call a handler function that returns a new named function (a closure) to act as the listener. You can then go through your tasks, and remove the listener when they're complete.
const board = document.querySelector('#board');
board.addEventListener('click', handler(), false);
function handler() {
// This is the function the listener uses
return function listener(e) {
console.log(e.target);
console.log('Task one');
console.log('Task two');
board.removeEventListener('click', listener);
console.log('The button no longer works.');
}
}
<button id="board">Board</button>
So I am trying to call a function outside of my
window.addEventListener('deviceorientation', function (event) {
console.log(event.alpha + ' : ' + event.beta + ' : ' + event.gamma);
this.adjustHeading(event.alpha);
})
the function that I am trying to call:
adjustHeading: function (heading) {
this.map.getModel().setViewRotation(heading, false);
}
The whole js:
(function ($) {
'use strict';
$.widget("symfony.GpsPosition", {
//lots of code//
listenForDeviceOrientation: function() {
window.addEventListener('deviceorientation', function (event) {
console.log(event.alpha + ' : ' + event.beta + ' : ' + event.gamma);
this.adjustHeading(event.alpha);
})},
adjustHeading: function (heading) {
this.map.getModel().setViewRotation(heading, false);
}
});
}(jQuery));
My problem is, that the this.adjustHeading(event.alpha); call from the window.eventListener does not work because adjustHeading() is not available in the windows scope.
Is there any way to somehow bypass that and get access to the JS properties in the same file?
I am using smyfony and openlayers for the mapview, if that is helpfull at all.
The issue is because your code expects this in the event handler function to be a reference to the settings object you provide to $.widget. However in the event handler the scope has been changed so that this refers to the object you attached the event to, the window in this case.
To fix this you could either use an ES6 arrow functions to define the event handlers, as these do not affect the scope of the handler:
listenForDeviceOrientation: () => window.addEventListener('deviceorientation', e => this.adjustHeading(e.alpha)),
adjustHeading: heading => this.map.getModel().setViewRotation(heading, false);
Or alternatively you could 'cache' the this reference in a variable declared in the outer scope, to be used within the event handler:
$.widget("symfony.GpsPosition", {
let _this = this;
// lots of code...
listenForDeviceOrientation: function() {
window.addEventListener('deviceorientation', function(event) {
_this.adjustHeading(event.alpha);
})
},
adjustHeading: function(heading) {
_this.map.getModel().setViewRotation(heading, false);
}
});
The former is more succinct but is not supported in IE, so the best choice for you would come down to what browsers you're required to support.
I recently have been upgrading the Phonegap to the latest version and now it forces me to follow the Chrome's Content Security Policy which in a way is good. But now I am forced to remove the all the onclick handlers in the HTML code and add them in the jquery handler some$(document).ready(function(evt){
$('#addRecordBtn').on('click', function(){
alert("Adding Record");
AddValueToDB();
});
$('#refreshBtn').on('click', function(){
alert("Refresh Records");
ListDBValues();
});
});
But as per what my app is scaled upto I feel that there will be too many of these handlers. Is there an example which shows maintenance of such handlers and a proper way or proper place of defining such handlers.
Here's an idea. You could make an object that stores all of the functions that also knows how to give up the function
var handlers = {
getHandler: function (str) {
return this[str];
},
'#addRecordBtn': function () {
alert("Adding Record");
AddValueToDB();
},
'#refreshBtn': function () {
alert("Refresh Records");
ListDBValues();
}
};
Then apply all of your handlers using this form.
$('#addRecordBtn').on('click', handlers.getHandler('#addRecordBtn'));
$('#refreshBtn').on('click', handlers.getHandler('#refreshBtn'));
Optimization Time if you want to get really fancy and you assign a unique ID to every button as convention
var handlers = {
defer: function () {
return function (){
handlers[$(this).attr('id')](arguments);
};
},
registerHandlers: function () {
for (var key in this) {
if (this.hasOwnProperty(key) && typeof(key) === "string") {
$('#' + key).on('click', this.defer());
}
}
},
'addRecordBtn': function () {
alert("Adding Record");
AddValueToDB();
},
'refreshBtn': function () {
alert("Refresh Records");
ListDBValues();
}
};
call it with
$('#addRecordBtn').on('click', handlers.defer());
$('#refreshBtn').on('click', handlers.defer());
or register everything automatically
handlers.registerHandlers();
Here is a fiddle of my solution
Do you look for something like this?
$('[data-clickhandler]').on('click', function(e) {
var $btn = $(e.currentTarget);
var handler = $btn.data('clickhandler');
alert('Refresh ' + handler);
window[handler] && window[handler](e);
e.preventDefault();
});
Now your elements can specify their clickhandler like so:
<a data-clickhandler="AddValueToDB" href="">...</a>
Or so:
<span data-clickhandler="ListDBValues">...</span>
I stopped at this issue 4h, if ignores my bool when Google maps event invoked. I need to give in parameters different data. Maybe on the world someone know why?
console.log throws after click in same time:
true before click
stack.html:56[object HTMLDivElement]in listener
stack.html:62[object HTMLDivElement]bunga bunga
broken bool:
this.b = true;
...
console.log(this.b + " beafore click");
this.mapListener = google.maps.event.addListener(map, 'click', function(e) {
console.log(this.b + "in listener");
if (this.b==true) {
console.log(this.b + "true works");
tools[type](e.latLng, last_marker_origin);
this.b = false;
} else {
console.log(this.b + "bunga bunga");
//tools[type](e.latLng);
}
});
this refers to "properties" in my object default set on false but when I change option it is flag to true.
I go to sleep for now. I'll answer in the morning.
Your problem is that this is no longer a valid reference to your properties. The simplest way to deal with your specific problem is to change the code:
this.b = true;
var props = this;
console.log(this.b + " beafore click"); //Notice that "this" is still valid here
this.mapListener = google.maps.event.addListener(map, 'click', function(e) {
console.log(props.b + "in listener");
if (props.b == true) {
console.log(props.b + "true works");
tools[type](e.latLng, last_marker_origin);
props.b = false;
} else {
console.log(props.b + "bunga bunga");
//tools[type](e.latLng);
}
});
The fundamental problem is that the code that actually makes the call to your callback function is in a completely different scope, so the meaning of this has changed when that code runs. Setting up a reference and putting that in the code will fix your problem.
The problem here is the scope of this. When you are inside the click event handler function this does no longer refer to your properties object, but to the event handler. The event handler is a so called closure.
There are two possible solutions for your problems.
Use a local variable (var b instead of this.b) for your value, local variables are copied in a closure, so the value can be used inside and outside of the closure:
var b = true;
console.log(b + " beafore click");
this.mapListener = google.maps.event.addListener(map, 'click', function(e) {
console.log(b + "in listener");
if (b==true) {
console.log(b + "true works");
tools[type](e.latLng, last_marker_origin);
b = false;
} else {
console.log(b + "bunga bunga");
//tools[type](e.latLng);
}
});
Save this in a local variable, this is a very common technique to avoid scoping problems:
//save this in a locale variable, now 'me' provides access to this scope
var me = this;
me.b = true;
console.log(me.b + " beafore click");
this.mapListener = google.maps.event.addListener(map, 'click', function(e) {
console.log(me.b + "in listener");
if (me.b==true) {
console.log(me.b + "true works");
tools[type](e.latLng, last_marker_origin);
me.b = false;
} else {
console.log(me.b + "bunga bunga");
//tools[type](e.latLng);
}
});
I'm building a webpage that is composed of several controls, and trying to come up with an effective somewhat generic client side sibling control communication model. One of the controls is the menu control. Whenever an item is clicked in here I wanted to expose a custom client side event that other controls can subscribe to, so that I can achieve a loosely coupled sibling control communication model.
To that end I've created a simple Javascript event collection class (code below) that acts as like a hub for control event registration and event subscription. This code certainly gets the job done, but my question is is there a better more elegant way to do this in terms of best practices or tools, or is this just a fools errand?
/// Event collection object - acts as the hub for control communication.
function ClientEventCollection()
{
this.ClientEvents = {};
this.RegisterEvent = _RegisterEvent;
this.AttachToEvent = _AttachToEvent;
this.FireEvent = _FireEvent;
function _RegisterEvent(eventKey)
{
if (!this.ClientEvents[eventKey])
this.ClientEvents[eventKey] = [];
}
function _AttachToEvent(eventKey, handlerFunc)
{
if (this.ClientEvents[eventKey])
this.ClientEvents[eventKey][this.ClientEvents[eventKey].length] = handlerFunc;
}
function _FireEvent(eventKey, triggerId, contextData )
{
if (this.ClientEvents[eventKey])
{
for (var i = 0; i < this.ClientEvents[eventKey].length; i++)
{
var fn = this.ClientEvents[eventKey][i];
if (fn)
fn(triggerId, contextData);
}
}
}
}
// load new collection instance.
var myClientEvents = new bsdClientEventCollection();
// register events specific to the control that owns it, this will be emitted by each respective control.
myClientEvents.RegisterEvent("menu-item-clicked");
Here is the part where this code above is consumed by source and subscriber controls.
// menu control
$(document).ready(function()
{
$(".menu > a").click( function(event)
{
//event.preventDefault();
myClientEvents.FireEvent("menu-item-clicked", $(this).attr("id"), null);
});
});
<div style="float: left;" class="menu">
<a id="1" href="#">Menu Item1</a><br />
<a id="2" href="#">Menu Item2</a><br />
<a id="3" href="#">Menu Item3</a><br />
<a id="4" href="#">Menu Item4</a><br />
</div>
// event subscriber control
$(document).ready(function()
{
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged);
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged2);
myClientEvents.AttachToEvent("menu-item-clicked", menuItemChanged3);
});
function menuItemChanged(id, contextData)
{
alert('menuItemChanged ' + id);
}
function menuItemChanged2(id, contextData)
{
alert('menuItemChanged2 ' + id);
}
function menuItemChanged3(id, contextData)
{
alert('menuItemChanged3 ' + id);
}
jQuery's event system can pass additional handler parameters when you trigger events. We also separate the control namespace from jQuery selectors by creating a registry that maps control names to selectors. To deal with handlers binding to a control before the control is registered, we implement a binding delay mechanism.
var controls = {};
(function ControlRegistry(controls) {
controls.items = {};
function bindNow(selector, event, eventData, handler) {
$(selector).bind(event, eventData, handler);
}
function delayBinding(queue, event, eventData, handler) {
queue.push([event, eventData, handler]);
}
function bindAll(queue, selector) {
for (var i=0; i<queue.length; ++i) {
var args = queue[i];
args.unshift(selector);
bindNow.apply(controls, args);
}
}
controls.register = function (name, selector) {
if (typeof this.items[name] == 'object') {
bindAll(this.items[name], selector);
}
this.items[name] = selector;
};
controls.bind = function (control, event, eventData, handler) {
jQuery.isFunction( eventData ) {
handler = eventData;
eventData = null;
}
switch (typeof this.items[control]) {
case 'undefined':
this.items[control] = [];
// FALLTHRU
case 'object':
delayBinding(this.items[control], event, eventData, handler);
break;
case 'string':
bindNow(this.items[control], event, eventData, handler);
break;
}
}
})(controls);
$(document).ready(function()
{
controls.register('menuItem', '.menu > a');
$(".menu > a").click( function(event)
{
$(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);
});
});
Elsewhere:
function menuItemChanged(evt, id, animal, speech)
{
alert('menuItemChanged ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
function menuItemChanged2(evt, id, animal, speech))
{
alert('menuItemChanged2 ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
function menuItemChanged3(evt, id, animal, speech))
{
alert('menuItemChanged3 ' + id
+ '\nThe ' + animal + ' says "' + speech + '."');
}
$(document).ready(function()
{
controls.bind('menuItem', "menu-item-clicked", menuItemChanged);
controls.bind('menuItem', "menu-item-clicked", menuItemChanged2);
controls.bind('menuItem', "menu-item-clicked", menuItemChanged3);
});
Update
if you include the restriction that a control be registered before handlers are bound to its events, the control registry can be vastly simplified:
var controls = {
register: function (name, selector) {
if (typeof this[name] != 'function') {
this[name] = selector;
}
};
};
...
controls.register('menuItem', '.menu > a');
$(document).ready(function()
{
$(".menu > a").click( function(event)
{
$(this).trigger("menu-item-clicked", [$(this).attr("id"), 'cow', 'moo']);
});
});
...
$(document).ready(function()
{
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged);
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged2);
$(controls.menuItem).bind("menu-item-clicked", menuItemChanged3);
});
This is a reasonable restriction, as you can register early (within the script for the control) and bind late (in $(document).ready).
My original solution ended up being the right one for me because it achieves the loose coupling I was after in a straight-forward and simple way.