Calling function from outside window.EventListener - javascript

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.

Related

Stop event propagation applying on same event object in backbone

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

Managing and Maintaining too many on click jquery handlers

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>

Calling a plugin .on() load using jQuery

I am calling some jQuery plugins that attaches themselves to element on DOM ready. These plugins manipulate the DOM when certain events has occurred (click, change etc,)
$("body").find("input[type='checkbox']").checkbox();
Above works fine on DOM ready. However, if I'm loading some HTML from an AJAX call I have to use .on() to guarantee events gets bound consistently.
The question is which event I should bind the plugin to? I have tried below and it doesn't seem to respond.
$("body").on("load", "input[type='checkbox']", function(){
$(this).checkbox();
});
Here's the checkbox() plugin I'm referring to above. If that's any help. :)
'use strict';
define(['jquery'], function($){
return function(){
$.fn.checkbox = function (options) {
options = options || {};
var defaults = {
'className': 'jquery-checkbox',
'checkedClass': 'jquery-checkbox-on'
};
var settings = jQuery.extend(defaults, options);
return this.each(function () {
var self = jQuery(this);
var replacement = jQuery(
'<span class="' + settings.className + '-wrapper">' +
'<a class="' + settings.className + '" href="#" name="' + self.attr('id') + '"></a>' +
'</span>');
var element = jQuery('a', replacement);
if (self.prop('checked')) {
element.addClass(settings.checkedClass);
}
element.on('click', function (event) {
event.preventDefault();
event.stopPropagation();
var input = jQuery('input#' + jQuery(this).attr('name'), replacement.parent());
if (input.prop('checked')) {
input.removeAttr('checked');
} else {
input.prop('checked', true);
}
input.trigger('change');
return false;
});
element.on('focusin', function (event) {
$(this).addClass('checkbox-focus');
});
element.on('focusout', function (event) {
$(this).removeClass('checkbox-focus');
});
element.on("keypress", function(e){
if ( e.which === 32 ){ self.prop('checked', !self.prop('checked')).change(); }
});
self.on('change', function (event) {
var input = jQuery(this);
if (input.prop('checked')) {
jQuery('a[name=' + input.attr('id') + ']', replacement.parent()).addClass(settings.checkedClass);
} else {
jQuery('a[name=' + input.attr('id') + ']', replacement.parent()).removeClass(settings.checkedClass);
}
return true;
});
self.css({
'position': 'absolute',
'top': '-200px',
'left': '-10000px'
}).before(replacement);
});
}
};
});
You appear to want to apply add-ins to elements that have been loaded dynamically. That is not what 'on' is for.
Delegated events listen for specific events (like "click") at a parent/ancestor element then filter the possible recipients, then executes the supplied function against any matching elements that caused the event.
You actually need to apply the add-in code after your Ajax load completes.
Example:
In the success part of your ajax load, apply the addin:
$("input[type='checkbox']").checkbox();
If you loaded a specific part of the screen (likely), then target the selector at that element:
e.g.
$("#myloadcontainer input[type='checkbox']").checkbox();

How to use bool in Google maps listener?

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);
}
});

Using a loop variable inside a function, javascript scoping confusion

I have built a dropdown menu system, everything works when tested independently, the problem I have is in the code below. I use the jQuery ready function to build the menu bar from an external array (menubar[]). Here I am trying to get the mouseover event to call the dropdown() function, but using a different argument for each anchor tag.
So rolling over the first should call dropdown(0), the second dropdown(1) and so on.
$(document).ready(function () {
for (i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
var a = $(declaration).mouseover(function () {
dropdown(i);
}).mouseout(function () {
activeTimer = setTimeout("removedropdowns()", 100);
});
$("#menu").append(a);
}
});
The code is calling dropdown(6); on each rollover. How can I pass the loop variable (i) into the mouseover function as a literal/static value!
I got this working fine in FF by using
.attr('onMouseOver','javascript:dropdown('+i+');')
but that wasn't firing for some versions of IE, so I switched to the jQuery mouseover, which fires, but I have the issue above :(
Your actual problem is that each of your mouseover callbacks uses the same i you increase i all the way up to 6, the callbacks still point to the same i and therefore all use 6 as the value.
You need to make a copy of the value of i, you can do this by using an anonymous function.
$(document).ready(function () {
// you should use (for(var i = 0, l = menubar.length; i < l; i++) here in case menubar is an array
for (var i in menubar) {
var declaration = '<a href="' + baseurl + '/' + menubar[i].url +
'" class="menutitle">' + menubar[i].name + '</a>';
(function(e) { // e is a new local variable for each callback
var a = $(declaration).mouseover(function () {
dropdown(e);
}).mouseout(function () {
activeTimer = setTimeout(removedropdowns, 100); // don't use strings for setTimeout, since that calls eval
});
$("#menu").append(a);
})(i); // pass in the value of i
}
});
$(function() {
$(menubar).each(function(i){
$("#menu").append('' + menubar[i].name + '');
});
$("#menu a").hover(
function(){
dropdown($(this).index());
},
function(){
activeTimer = setTimeout("removedropdowns()", 100);
}
);
});
First, don't use for..in but rather ordinary loop.
Second, I would just append the links first then apply the events later:
$(document).ready(function() {
for (var i = 0; i < menubar.length; i++) {
$("#menu").append('' + menubar[i].name + '');
}
$("#menu a").each(function(index) {
$(this).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
});
});
Have a look here and here.
To capture the current value of i, you need to pass it as a parameter to another function where it can be captured as a local variable:
Try using jQuery's each() function:
jQuery(function() {
jQuery.each(menubar, function(index, element) {
var declaration = '' + element.name + '';
var a = $(declaration).mouseover(function() { dropdown(index); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
});
});
In JavaScript, if you don't declare your variable, it is defined globally. To fix this, add "var" in front of your i looping variable like this. UPDATE: As Sime noticed (see comment), you also need to pass the variable into the function, otherwise you form a closure on the i.
$(document).ready(function() {
for(var i in menubar) {
var declaration = '' + menubar[i].name + '';
var a = $(declaration).mouseover(function(i) { dropdown(i); }).mouseout(function() { activeTimer = setTimeout("removedropdowns()", 100); });
$("#menu").append(a);
}
});

Categories