How do I access object that is inside an event - javascript

I have this object that carries some functions. At some point, I would like to hold an event and call this object within that event, but only if you call this object using this, is there any way to call it without this?
I haven't tried anything yet, because I couldn't find anything to help.
const cantaVideoModal = {
click: null,
target: null,
urlVideo: null,
config: function (c) {
this.click = c.click;
this.target = c.target;
this.urlVideo = c.urlVideo;
this.init();
},
init: function () {
this.click = (this.click) ? document.querySelector(this.click) : null;
this.target = (this.target) ? document.querySelector(this.target) : null;
let btnCloseVideo = document.querySelector('[data-close-modal]');
if(btnCloseVideo){
btnCloseVideo.addEventListener('click', function(){
//call modalAction object here using this
})
}
},
modalAction: function (act) {
let elementClick = this.click;
let elementtarget = this.target;
if (elementClick) {
elementClick.addEventListener('click', function (e) {
e.preventDefault();
if (elementtarget) {
if(act === "toggle")
elementtarget.classList.toggle('in');
if(act === "show")
elementtarget.classList.add('in');
if(act === "hide")
elementtarget.classList.remove('in');
}
})
}
}
}

The reason you cannot use this is because you are in a function inside a addEventListener. I'm not sure why, but you can create a new object that refer to the current object, which can be used inside the annonymous functon. For more information on why you can't use this inside a addEventListener see this answer
const cantaVideoModal = {
click: null,
target: null,
urlVideo: null,
config: function (c) {
this.click = c.click;
this.target = c.target;
this.urlVideo = c.urlVideo;
this.init();
},
init: function () {
this.click = (this.click) ? document.querySelector(this.click) : null;
this.target = (this.target) ? document.querySelector(this.target) : null;
let btnCloseVideo = document.querySelector('[data-close-modal]');
if(btnCloseVideo){
// we create a new variable that refer to the current content.
var self = this
btnCloseVideo.addEventListener('click', function() {
//call modalAction object here using self.
self.modalAction(/* some parameters */);
})
}
},
modalAction: function (act) {
let elementClick = this.click;
let elementtarget = this.target;
if (elementClick) {
elementClick.addEventListener('click', function (e) {
e.preventDefault();
if (elementtarget) {
if(act === "toggle")
elementtarget.classList.toggle('in');
if(act === "show")
elementtarget.classList.add('in');
if(act === "hide")
elementtarget.classList.remove('in');
}
})
}
}
}
P.S. I tried using an arrow function but couldn't get the code to work properly.

Related

How to execute nested Javascript function

I am troubleshooting a slider problem at the moment, however, I don't know javascript that well, I have isolated the .js file that is responsible for the slider functioning, there is a destroy function that I would like to fire off, the code looks like this
(function ($) {
$.pixelentity = $.pixelentity || {version: '1.0.0'};
$.pixelentity.peBackgroundSlider = {
conf: {
api: false,
wait: false
},
paused: false
};
function PeBackgroundSlider(target, conf) {
...
function destroy() {
prevColor = currentColor = currentBW = jwindow = jthis = undefined;
target.data("peBackgroundSlider", null);
target = undefined;
}
}
How would I fire off the destroy function in this scenario?
You can't as it is right now.
To call it you must "export" it as follows:
function PeBackgroundSlider(target, conf) {
...
function destroy() {
prevColor = currentColor = currentBW = jwindow = jthis = undefined;
target.data("peBackgroundSlider", null);
target = undefined;
}
return { "destroy": destroy };
}
From the caller:
var ret = PeBackgroundSlider();
Now you can do:
ret.destroy();
Or, more concise:
return destroy;
And:
innerDestroy = PeBackgroundSlider();
And finally:
innerDestroy();

Integrating TextExt in SlickGrid custom editor

i want to implement a custom editor in SlickGrid using TextExt but I'm having trouble doing so.
I have two different lists args.column.names and `args.column.values.
I want the tags to show the selected names but to post the list of the corresponding ids.
Here is a first draft, I don't really see how to manage that.
Can someone help me figure out what to write in these functions to match what I'm trying to do ?
function AutoCompletedTextField(args) {
var $input;
var defaultValue;
var scope = this;
this.init = function () {
$input = $("<textarea class='textarea' rows='1'></textarea>")
.appendTo(args.container)
.bind("keydown.nav", function (e) {
if (e.keyCode === $.ui.keyCode.LEFT || e.keyCode === $.ui.keyCode.RIGHT) {
e.stopImmediatePropagation();
}
})
.focus()
.select();
$('.textarea').textext({
plugins: 'tags autocomplete arrow'
}).bind('getSuggestions', function (e, data) {
var list = args.column.names,
textext = $(e.target).textext()[0],
query = (data ? data.query : '') || '';
$(this).trigger('setSuggestions', { result: textext.itemManager().filter(list, query) });
});
};
this.destroy = function () {
$input.remove();
};
this.focus = function () {
$input.focus();
};
this.getValue = function () {
return $input.textext()[0].hiddenInput().val();
};
this.setValue = function (val) {
$input.textext()[0].hiddenInput().val(val)
};
this.loadValue = function (item) {
$input[0].defaultValue = item[args.column.field];
$input.select();
};
this.serializeValue = function () {
return $input[0].defaultValue;
};
this.applyValue = function (item, state) {
item[args.column.field] = state;
};
this.isValueChanged = function () {
return (!($input.textext()[0].hiddenInput().val() == "" && defaultValue == null)) && ($input.textext()[0].hiddenInput().val() != defaultValue);
};
this.validate = function () {
if (args.column.validator) {
var validationResults = args.column.validator($input.val());
if (!validationResults.valid) {
return validationResults;
}
}
return {
valid: true,
msg: null
};
};
this.init();
}
You may want to try my repo, it's updated for the latest jQuery and has a lot of fixes and enhancements.
One of those enhancements is a Select2 editor, which is very similar to what you're trying to do. I think if you check that out it will be clear what is needed.
There's an example here.
As a bonus, there also a mechanism to allow certain keycodes to bubble through to the grid rather than be captured by the editor. This can be useful for up-arrow, etc. This isn't in the MLeibman branch.

Set an object function externally that can be called internally

I want to be able to set a function onbroadcast in SpeechRecognition after I create a new SpeechRecognition object so that I can call this function internally if certain conditions are met.
I would like to be able to set it in the same way that you would set something like onerror in webkitSpeechRecognition. When I look at onerror in the Developer Tools it looks like it might be done via some sort of getter/setter like what is described here but I can't be certain.
Is this possible?
recognition.js:
var SpeechRecognition = function () {
var recognitionObject = new webkitSpeechRecognition();
recognitionObject.onresult = function (event) {
if(event.results.length > 0) {
if (onbroadcast !== null && onbroadcast === 'function') {
onbroadcast('there are results');
}
}
}
recognitionObject.onerror = function (event) {
console.log(event);
}
recognitionObject.onend = function (event) {
console.log(event);
}
recognitionObject.start();
}
SpeechRecognition.prototype.onbroadcast = null;
main.js:
var sr = new SpeechRecognition();
sr.onbroadcast = function(msg) {
document.getElementById('id') = msg;
}
You need to refer to onbroadcast as a property of your instance (this.onbroadcast). It doesn't magically become available as a variable inside the constructor scope.
function SpeechRecognition() {
var self = this; // a reference to the instance
var recognitionObject = new webkitSpeechRecognition();
recognitionObject.onresult = function (event) {
if (event.results.length > 0 && typeof self.onbroadcast === 'function') {
self.onbroadcast('there are results');
// ^ a property access
}
};
recognitionObject.onerror = recognitionObject.onend = function (event) {
console.log(event);
};
recognitionObject.start();
}

rewriting localStorage javascript for chrome.local.set

I have this code which is working with the localStorage html5 calls. However it has to be rewritten for a Chrome Desktop app and I can't figure out how to port it over.
window.fakeStorage = {
_data: {},
setItem: function (id, val) {
return this._data[id] = String(val);
},
getItem: function (id) {
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
},
removeItem: function (id) {
return delete this._data[id];
},
clear: function () {
return this._data = {};
}
};
function LocalScoreManager() {
this.key = "bestScore";
var supported = this.localStorageSupported();
this.storage = supported ? window.localStorage : window.fakeStorage;
}
LocalScoreManager.prototype.localStorageSupported = function () {
var testKey = "test";
var storage = window.localStorage;
try {
storage.setItem(testKey, "1");
storage.removeItem(testKey);
return true;
} catch (error) {
return false;
}
};
LocalScoreManager.prototype.get = function () {
return this.storage.getItem(this.key) || 0;
};
LocalScoreManager.prototype.set = function (score) {
this.storage.setItem(this.key, score);
};
The error I get says "window.localStorage is not available in packaged apps. Use chrome.storage.local instead."
My attempt to rewrite it was this so far.. but it is breaking somewhere along the way.
$(document).ready(function() {
$("body").bind('keyup', function() {
var number = $(".best-container").val();
if(number == 'undefined'){
var number = "0";
}
chrome.storage.local.set({'bestScore': number});
});
chrome.storage.local.get('bestScore', function (result) {
hello= result.bestScore || 0;
$(".best-container").val(hello);
});
});
Porting localStorage to chrome.storage has one important pitfall: chrome.storage methods are asynchronous whereas localStorage access is synchronous.
That means: If you try to get a value from chrome.storage before the callback of the set method has been called, the value will still be undefined
Wrong way:
chrome.storage.local.set({'key': value});
...
chrome.storage.local.get('key', function(items) {
if(items.key) // won't be able to find the key
alert(items.key);
});
Correct way:
chrome.storage.local.set({'key': value}, function() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
});
or rather:
chrome.storage.local.set({'key': value}, function() {
doFurtherStuff();
});
function doFurtherStuff() {
...
chrome.storage.local.get('key', function(items) {
if(items.key)
alert(items.key); // will be "value"
});
}

How to call public method from a event handler

I have the function below.
Everything works fine except for the Push, Pop and Remove method. These method should be called by the event-handler. This event is fired by the Google Maps API.
The problem is that when the event is fired, these method are not found. I have a "Push is not defined" error message.
I tried with this but that's not working.
How do I call the public method from the event handler?
function Track(mapContainer) {
var map = mapContainer;
var points = new Array();
var isEditMode = false;
var clickListener;
this.Push = function(point) { ... }
this.Pop = function() { ... }
this.Remove = function(point) { ... }
//Enable / disable the marker placements
this.PlaceWaypoint = function(isPlacing) {
if (isPlacing != true) {
if (clickListener != null) {
google.maps.event.removeListener(clickListener);
clickListener = null;
}
} else {
clickListener = map.AddEvent("click", function(event) {
if (!IsDoubleClick()) {
var point = map.PlaceMarker(new WayPoint(event.latLng))
point.RemoveListener(function() { Remove(point); });
Push(point);
} else {
Pop();
}
});
}
}
}
You've got a closure/binding problem. One convention that is frequently used it to assign a variable called self of that, which can later be used in place of this, thanks to the closure properties of JS.
function Track(mapContainer) {
var map = mapContainer,
points = new Array(),
isEditMode = false,
clickListener,
// Make a variable self that points to this, that can be used inside closures
// where the original context is lost
self = this;
this.Push = function(point) { ... }
this.Pop = function() { ... }
this.Remove = function(point) { ... }
//Enable / disable the marker placements
this.PlaceWaypoint =
function(isPlacing) {
if (isPlacing != true) {
if (clickListener != null) {
google.maps.event.removeListener(clickListener);
clickListener = null;
}
} else {
clickListener = map.AddEvent("click", function(event) {
if (!IsDoubleClick()) {
var point = map.PlaceMarker(new WayPoint(event.latLng))
point.RemoveListener(function() { Remove(point); });
// Use the closure reference self instead of this
self.Push(point);
} else {
// Use the closure reference self instead of this
self.Pop();
}
});
};
}
this always refers to the context of the current function, so if you use this in your event handler it refers to that function calls this, not the this in your Track function.
To create a closure that accesses the this of an outer scope, you need to assign that this to a new variable which can be accessed from the inner function:
var self = this;
this.PlaceWaypoint = function(isPlacing) {
// ...
self.Pop();
// ...
}
First of all Pop and Push is not global, second this in the inner scope has another meaning. So you can use closure and rename the "this" to variable of more global scope.
function Track(mapContainer) {
//....
var $this = this;
//Enable / disable the marker placements
this.PlaceWaypoint = function(isPlacing) {
if (isPlacing != true) {
if (clickListener != null) {
google.maps.event.removeListener(clickListener);
clickListener = null;
}
} else {
clickListener = map.AddEvent("click", function(event) {
if (!IsDoubleClick()) {
var point = map.PlaceMarker(new WayPoint(event.latLng))
point.RemoveListener(function() { $this.Remove(point); });
$this.Push(point);
} else {
$this.Pop();
}
});
}
}
}

Categories

Resources