How can a method be called dynamically from a class in ES6?
In ES5 and lower I can do this with the following. JSFiddle example
var App = function() {
var that = this;
this.init = function() {
var elements = document.getElementsByClassName('call-method');
for(var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', function() {
that['method' + this.dataset.method](this);
});
}
};
this.methodOne = function(element) {
console.log(element.innerText);
};
this.methodTwo = function(element) {
console.log(element.innerText);
};
};
(function() {
var app = new App();
app.init();
}());
When I try to do the same in ES6 I get an error Uncaught TypeError: not a function. Is this possible in ES6 or am I doing something wrong here? JSFiddle example
'use strict';
class App {
constructor() {
var elements = document.getElementsByClassName('call-method');
for(var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', function() {
this.constructor['method' + this.dataset.method](this); // Uncaught TypeError: not a function
App['method' + this.dataset.method](this); // Uncaught TypeError: not a function
});
}
}
methodOne(element) {
console.log(element.innerText);
}
methodTwo(element) {
console.log(element.innerText);
}
}
(function() {
new App();
}());
I think you're misunderstanding how ES6 classes work. Your first strategy is not going to work, because this.constructor is a method, not a reference to any constructor class. The second won't work, because that would only reference a static method.
Instead:
constructor() {
var elements = document.getElementsByClassName('call-method');
for(var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', (e) => {
this['method' + e.target.dataset.method](e);
});
}
}
Also arrow functions are going to be a better way to bind the event.
Edit: Updated your fiddle to show it in action - http://jsfiddle.net/dqk8n3xk/3/
You can do the following:
'use strict';
class App {
constructor() {
var that = this;
var elements = document.getElementsByClassName('call-method');
for(var i = 0; i < elements.length; i++) {
elements[i].addEventListener('click', function() {
that['method' + this.dataset.method](this);
});
}
}
methodOne(element) {
console.log(element.innerText);
}
methodTwo(element) {
console.log(element.innerText);
}
}
(function() {
new App();
}());
as shown in this jsfiddle
Related
Here i add a delay in javascript forloop using $timeout. Unexpectedly i got an error saying
ReferenceError $timeout is not defined. i am new to angularjs please help me.
PLNKR
function CompLibrary() {
return {
init: init
}
function init(dependencies, controller) {
dependencies.push(controller);
angularApp.controller('MainCtrl', dependencies);
}
}
var compX = CompLibrary();
compX.init(deps, _controller);
function _controller() {
var ViewModel = this;
ViewModel.search = "Name";
ViewModel.quantity = 1;
for(var i = 0; i < 4; i++) {
(function(i){
$timeout(function() {
ViewModel.quantity++;
}, i * 2000);
})(i); // Pass in i here
}
}
You have to inject the $timeout into the controller function.
function _controller($timeout) { ... }
Please see updated Plunkr
var deps = [];
var angularApp = angular.module('plunker',[]);
function CompLibrary() {
return {
init: init
}
function init(dependencies, controller) {
dependencies.push('$timeout');
dependencies.push(controller);
angularApp.controller('MainCtrl', dependencies);
}
}
var compX = CompLibrary();
compX.init(deps, _controller);
function _controller($timeout) {
var ViewModel = this;
ViewModel.search = "Name";
ViewModel.quantity = 1;
for(var i = 0; i < 4; i++) {
(function(i){
$timeout(function() {
ViewModel.quantity++;
}, i * 2000);
})(i); // Pass in i here
}
}
By injecting $timeout in the controller function we can solve this problem.
i'm try to make something and i made this piece of code,but when i press the botton it's happend for a sec and then disappear,am i donig passing the arguments wrong or something?
here the code is:
{
var fil1;
var rtextDiv;
for (var i = 0; i < dmsg.getElementsByClassName('refilter').length; i++) {
var refilterInput = dmsg.getElementsByClassName('refilter')[i];
refilterInput.addEventListener('keyup', firstfilter(rtextDiv, fil1,refilterInput));
}
};
function firstfilter(e, rtextDiv, fil1, refilterInput) {
rtextDiv = refilterInput.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
fil1 = filteredPropertiesTable(res, refilterInput.value);
rtextDiv.appendChild(fil1);
};
edited as the comment said:
{
var fil1;
var rtextDiv;
for (var i = 0; i < dmsg.getElementsByClassName('refilter').length; i++) {
var refilterInput = dmsg.getElementsByClassName('refilter')[i];
refilterInput.addEventListener('keyup', function()
{firstfilter(rtextDiv,fil1,refilterInput)(rtextDiv, fil1,refilterInput)});
);
}
};
function firstfilter(e, rtextDiv, fil1, refilterInput) {
rtextDiv = refilterInput.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
fil1 = filteredPropertiesTable(res, refilterInput.value);
rtextDiv.appendChild(fil1);
};
is it true know?can i pass argument that way?
Here you are actually executing the handler:
refilterInput.addEventListener('keyup', firstfilter(rtextDiv, fil1,refilterInput));
You should just present the handler name:
refilterInput.addEventListener('keyup', firstfilter);
And the handler can be improved:
function firstfilter(e) {
var rtextDiv = this.parentNode.parentNode.getElementsByClassName('rtext')[0];
while (rtextDiv.firstChild) {
rtextDiv.removeChild(rtextDiv.firstChild);
}
var fil1 = filteredPropertiesTable(res, this.value); // you didn't say what is res
rtextDiv.appendChild(fil1);
};
how can I set the object (which is part of the buttons array) as a parameter within the addEventListener function? buttons[i] is not working..
Here is a part of the code:
var buttonNames = ["canteen","locations","floorplan","guestbook","pictures"];
var buttonDivNames = ["btn1","btn2","btn3","btn4","btn5"];
var buttons = [];
window.onload = function() {
for(var i = 0; i<buttonNames.length; i++) {
var obj = new Object();
obj.targetLink = buttonNames[i] + ".html";
obj.defaultImage = "img/buttons/"+buttonNames[i]+"_default.jpg";
obj.hoverImage = "img/buttons/"+buttonNames[i]+"_hover.jpg";
obj.div = document.getElementById(buttonDivNames[i]);
obj.divPicture = obj.div.getElementsByClassName("thumbnailPicture")[0];
obj.divLink = obj.div.getElementsByClassName("thumbnailLink")[0];
buttons.push(obj);
}
for(var i = 0; i<buttons.length; i++) {
buttons[i].divPicture.addEventListener("mouseover",function() { anotherFunction(buttons[i]) },false)
}
}
function anotherFunction(arg) {
console.log(arg.targetLink);
}
Thanks guys, this way it works:
for(var i = 0; i<buttons.length; i++) {
initButton(buttons[i]);
}
}
function initButton(arg) {
arg.divPicture.addEventListener("mouseover",function() {anotherFunction(arg);},false)
}
function anotherFunction(arg) {
console.log(arg.targetLink);
}
As pointed out in the comment section, you could use an IIFE to create a new scope, that holds the value of the current i:
for(var i = 0; i<buttons.length; i++) {
(function (i) {
buttons[i].divPicture.addEventListener("mouseover",function() { anotherFunction(buttons[i]) },false)
}(i));
}
or, even better, create a seperate function that handles the adding of the eventlistener:
function addEventlistenerToButton(button) {
button.divPicture.addEventListener("mouseover",function() { anotherFunction(button) },false)
}
// ....
for(var i = 0; i<buttons.length; i++) {
addEventlistenerToButton(buttons[i]);
}
In addition to that, you could also omit sending the button to the eventlistener completely and get the button from the event object directly:
for(var i = 0; i<buttons.length; i++) {
buttons[i].divPicture.addEventListener("mouseover", anotherFunction, false);
}
function anotherFunction(ev) {
ev = ev || window.event;
var src = ev.target || ev.srcElement;
console.log(src.parentNode);
}
First the following is the code of my own javascript library.
(function() {
var lib = {
elems: [],
getElem: function() {
var tmpElem = [];
for (var i = 0; i < arguments.length; i++)
tmpElem.push(document.getElementById(arguments[i]));
this.elems = tmpElem;
tmpElem = null;
return this;
},
html: function(txt) {
for (var i = 0; i < this.elems.length; i++)
this.elems[i].innerHTML = txt;
return this;
},
style: function(prob, val) {
for (var i = 0; i < this.elems.length; i++)
this.elems[i].style[prob] = val;
return this;
},
addEvent: function(event, callback) {
if (this.elems[0].addEventListener) {
for (var i = 0; i < this.elems.length; i++)
this.elems[i].addEventListener(event, callback, false);
} else if (this.elems[0].attachEvent) {
for (var i = 0; i < this.elems.length; i++)
this.elems[i].attachEvent('on' + event, callback);
}
return this;
},
toggle: function() {
for (var i = 0; i < this.elems.length; i++)
this.elems[i].style.display = (this.elems[i].style.display === 'none' || '') ? 'block' : 'none';
return this;
},
domLoad: function(callback) {
var isLoaded = false;
var checkLoaded = setInterval(function() {
if (document.body && document.getElementById)
isLoaded = true;
}, 10);
var Loaded = setInterval(function() {
if (isLoaded) {
clearInterval(checkLoaded);
clearInterval(Loaded);
callback();
}
}, 10);
}
};
var fn = lib.getElem;
for(var i in lib)
fn[i] = lib[i];
window.lib = window.$ = fn;
})();
Previously, I have used this way to use my own library, and works fine .
$.getElem('box').html('Welcome to my computer.');
But when updated the code of my own library, and I added
var fn = lib.getElem;
for(var i in lib)
fn[i] = lib[i];
To be using the element selector like this way
$('box').html('Welcome to my computer.');
But the problem began appear when added the updated code to clone the lib object TypeError: $(...).html is not a function.
And now I want to use the element selector like that
$('box').html('Welcome to my computer.');
instead of
$.getElem('box').html('Welcome to my computer.');
You create a variable fn which has a reference to "getElem" but since fn is not a property on your lib object then it means that when getElem refers to "this" it will be you global object which is propbably window.
Remove all the following 3 lines
var fn = lib.getElem;
for(var i in lib)
fn[i] = lib[i];
and then do this
window.$ = function () { return lib.getElem.apply(lib, arguments); };
This will allow getElem to be called as $ but maintaining "lib" as context.
Although I don't know exactly what you are trying to achieve with those additional lines, just by reading the code, lib.getElem does not have a function called html
lib does.
Hence, just var fn = lib; should do just fine.
There more ways to achieve this but the root cause is in your getElem() function: return this;
$ is a reference to that function. If you call $() it is called as a function and not as a method. Therefore this refers to window and window has, of course, no html() function.
You could do return lib; to fix the problem.
In the following code snippet, 'this.x()' can only be called in case 2 (see main()).
Also Bar unequals this in case 1, but is equal for case 2.
function Class_Bar() {
this.panel = null;
this.init = function () {
// do some stuff
this.panel = 20;
}
this.apply = function () {
alert(Bar == this);
Bar.x();
this.x();
}
this.x = function() {
alert("Some friendly message");
alert(Bar.panel);
}
}
var Bar = new Class_Bar();
function Class_Factory() {
this.factories = new Array();
this.add = function (init, apply) {
this.factories.push({"init":init, "apply":apply});
}
this.init = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["init"]();
}
}
this.apply = function () {
for (var i = 0; i < this.factories.length; ++i) {
this.factories[i]["apply"]();
}
}
}
var Factory = new Class_Factory();
function main() {
// Case 1
Factory.add(Bar.init, Bar.apply);
Factory.init();
Factory.apply();
// Case 2
Bar.init();
Bar.apply();
}
main();
http://pastebin.com/fpjPNphx
Any ideas how to "fix" / workaround this behaviour?
I found a possible solution, but it seems to be a "bad" hack.: Javascript: How to access object member from event callback function
By passing Bar.init, you're really only passing the function but not the information that it belongs to Bar (i.e. what the this value should be). What you can do is binding that information:
Factory.add(Bar.init.bind(Bar), Bar.apply.bind(Bar));