Cannot read property 'addEventListener' of undefined - javascript

function assign(id){
return document.getElementById(id) ;
}
var b = ['p','q','r','s','t','u','v'];
var a = ['fname','lname','email','password','r_password','g_m',"g_f"] ;
for (i=0;i<a.length;i++) {
var x = {} ;
x[b[i]] = assign(a[i]) ;
x[i].addEventListener('click', function() { alert(x[i].value) ;} ,false) ;
}
I want just array of variables and IDs assign with them in for loop .

You're expecting x[i] to return the DOM element you just stored, but you're storing the DOM element at x[b[i]], not x[i]. Be consistent, and that error will go away.
The code still won't work, though, because you're falling into the closure trap: When the click occurs, the event handler will use i as it is then, not as it was when the handler was created. So all the handlers will see i as a.length and fail.
I usually use a builder function to handle that:
for (i = 0; i < a.length; i++) {
var x = {};
x[b[i]] = asign(a[i]);
hookUp(i);
}
function hookUp(index) {
x[b[index]].addEventListener('click', function() {
alert(x[b[index]].value);
}, false);
}
hookUp uses index (which we don't change) rather than i. (I also took a guess at which to use, x[i] or x[b[i]].)
That said, if your goal is to alert the value of the element that was clicked, use this instead:
for (i = 0; i < a.length; i++) {
var x = {};
x[b[i]] = asign(a[i]);
x[b[i]].addEventListener('click', function() {
alert(this.value);
}, false);
}
or Event#currentTarget:
for (i = 0; i < a.length; i++) {
var x = {};
x[b[i]] = asign(a[i]);
x[b[i]].addEventListener('click', function(e) {
alert(e.currentTarget.value);
}, false);
}

function asign(id){
return document.getElementById(id) ;
}
var a = ['a','b','c'];
var b = ['fname','lname','email'] ;
for (i=0;i<a.length;i++) {
(function(i){
var x = {} ;
x[b[i]] = asign(a[i]) ;
x[b[i]].addEventListener('click', function() { alert(i+1) ;} ,false) ;
})(i);
}
<div id="a">Div 1</div>
<div id="b">Div 2</div>
<div id="c">Div 3</div>

Related

JavaScript remove an IIFE event listener

I'm trying to remove click events from a list of id's after adding them with an IIFE like this
function setupPlayer(player){
var squareState = {};
for (i = 0; i < allSquares.length; i++) {
if(allSquares[i].innerHTML === "") {
// set up a click event for each square
document.getElementById(allSquares[i].getAttribute('id')).addEventListener('click', (clickSquare)(i));
}
}
}
The clickSquare function returns
function clickSquare(i){
var num = i;
return function() {
document.getElementById(allSquares[num].getAttribute('id')).innerHTML=player;
}
}
Then I try to remove them with
function removeClickEvents(){
for (let i = 0; i < allSquares.length; i++) {
document.getElementById(allSquares[i].getAttribute('id')).removeEventListener('click', clickSquare);
}
}
I've tried naming the returned anonymous function and using removeEventListener on that to no avail.
To remove event listener from a DOM element you need to pass the same function you used while adding event listener, as the parameter.
In javascript when you create an object it creates a new instance of that object class, so it won't be equal to another object even if it is created with same parameters
Example:
{} != {} // returns true
[] != [] // returns true
Same goes with function, whenever you write function (){} it creates a new instance of Function class.
Example:
function a() {
return function b() {}
}
a() != a() // returns true
Solution:
So for you to be able to remove the event listeners, you will have to store the functions you have passed to addEventListener
var listeners = [];
function setupPlayer(player) {
var squareState = {};
for (i = 0; i < allSquares.length; i++) {
if(allSquares[i].innerHTML === "") {
listeners[i] = clickSquare(i);
document.getElementById(allSquares[i].getAttribute('id')).addEventListener('click', listeners[i]);
}
}
}
function clickSquare(i) {
var num = i;
return function() {
document.getElementById(allSquares[num].getAttribute('id')).innerHTML=player;
}
}
function removeClickEvents() {
for (let i = 0; i < allSquares.length; i++) {
if(listeners[i]) {
document.getElementById(allSquares[i].getAttribute('id')).removeEventListener('click', listeners[i]);
}
}
}
From your code where you are using
document.getElementById(allSquares[i].getAttribute('id'))
I am assuming that allSquares[i] is a DOM element already, your code can be more simplified
var listeners = [];
function setupPlayer(player) {
var squareState = {};
for (i = 0; i < allSquares.length; i++) {
if(allSquares[i].innerHTML === "") {
listeners[i] = clickSquare(i);
allSquares[i].addEventListener('click', listeners[i]);
}
}
}
function clickSquare(i) {
var num = i;
return function() {
allSquares[num].innerHTML=player;
}
}
function removeClickEvents() {
for (let i = 0; i < allSquares.length; i++) {
if(listeners[i]) {
allSquares[i].removeEventListener('click', listeners[i]);
}
}
}
The function is being called immediately at (clickSquare)(i). At code at Question allSquares appears to be the element itself, clickSquare function can be referenced directly and event.target can be used within event handler to reference the current element in allSquares collection
let player = 123;
setInterval(() => player = Math.random(), 1000);
onload = () => {
let allSquares = document.querySelectorAll("div[id|=square]");
let button = document.querySelector("button");
button.onclick = removeClickEvents;
function setupPlayer(player) {
var squareState = {};
for (let i = 0; i < allSquares.length; i++) {
if (allSquares[i].innerHTML === "click") {
// set up a click event for each square
allSquares[i].addEventListener('click', clickSquare);
}
}
}
function clickSquare(event) {
console.log(event.target);
event.target.innerHTML = player;
}
function removeClickEvents() {
for (let i = 0; i < allSquares.length; i++) {
allSquares[i].removeEventListener('click', clickSquare);
}
}
setupPlayer(player);
}
<div id="square-0">click</div>
<div id="square-1">click</div>
<div id="square-2">click</div>
<button>remove events</button>

javascript could not achieve the return

I had made this function, and this is new yet, and I don't really know how to handle this roughly.
var $;
(function() {
$ = function(e) {
return new query(e);
};
var query = function(e) {
var e = document.querySelectorAll(e), i;
for (i = 0; i < e.length; i++) {
this[i] = e[i];
}
this.length = e.length;
return this;
};
$.fn.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'block';
}
return this;
},
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
}
} ());
window.onload = function() {
$(".element").hasClass("someClass").hide();
}
So yeah, that's the code above. I think I have matched the class, but what the problem is, It's not returning the elements. I'm new to prototyping so please don't be harsh. I really need to fix this one. Please don't tell me to go and have jquery. I don't want to use that massive library just because I want some css selectors.
this inside the function isn't the current element.
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
and that is why your script do not return the element.
ALSO:
You tried to use jQuery's method to add your own functions in library, but actually this will not work, as you're writing pure js, not jquery, and you need to do many thing to make it work like jQuery.
$.fn.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'block';
}
return this;
},
hasClass: function (klass) {
var e = this, i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return t;
}
}
}
The problem here is this line: $.fn.prototype = {};. There is no $.fn in your code anywhere, so you cannot set a prototype property on it. (In jQuery, $.fn is set to jQuery.prototype, that's why you can set $.fn.someMethod.)
If you want the hasClass method to exists on your objects, you need to modify the prototype of query. Your $ function returns new query objects, so query is where the hasClass method should be.
Also, your hasClass method returns an array. Arrays do not have a method called hide. You need to have your hasClass method return a query object, so that you can continue to chain methods.
P.S. Shouldn't a hide method set the style.display to 'none'?
Anyway, your code should look like this:
var $;
(function() {
$ = function(e) {
return new query(e);
};
var query = function(e) {
var e = Array.isArray(e) ? e : document.querySelectorAll(e),
i;
for (i = 0; i < e.length; i++) {
this[i] = e[i];
}
this.length = e.length;
return this;
};
query.prototype = {
hide: function() {
var i;
for (i = 0; i < this.length; i++) {
this[i].style.display = 'none';
}
return this;
},
hasClass: function(klass) {
var e = this,
i;
var t = [];
for (i = 0; i < e.length; i++) {
var k = e[i].className;
var array = k.split(' ');
// If the element has the class, add it for return
if (array.indexOf(klass) > -1) {
t.push(e[i]);
}
}
// Return the list of matched elements
return new query(t);
}
}
}());
window.onload = function() {
$(".element").hasClass("someClass").hide();
}
<p class="someClass element">You can't see me!</p>
(Note: Array.isArray doesn't work in IE < 9.)
If all you want is to query for elements matching a condition and hide it based on element type, id or class name CSS query selectors along with querySelector() or querySelectorAll() api.
For your code sample the same can be rewritten as below
var elements = document.querySelectorAll("element.someClass");
for(var i = 0; i <elements.length; i++) {
elements[i].style.visibility = 'hidden';
}

JavaScript: How to set a parent object as a parameter within an addEventListener function?

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

When I make a clone of the object shows me an error message

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.

Javascript dynamic onChange event on select

I have this javascript snippet:
var selectName["id1","id2","id3"];
setOnClickSelect = function (prefix, selectName) {
for(var i=0; i<selectName.length; i++) {
var selId = selectName[i];
alert(selId);
$(selId).onchange = function() {
$(selId).value = $(selId).options[$(selId).selectedIndex].text;
}
}
}
But when I change value to my id1 element, the alert wrote me always "id3".
Can I fix it?
EDIT:
I've changed my snippet with these statements:
setOnChangeSelect = function (prefix, selectName) {
for(var i=0; i<selectName.length; i++) {
var selId = selectName[i];
$(selId).onchange = (function (thisId) {
return function() {
$(selId).value = $(thisId).options[$(thisId).selectedIndex].text;
}
})(selId);
}
}
But selId is always the last element.
This is caused by the behavior of javaScript Closure, selId has been set to the selectName[2] at the end of the loop and that's why you get 'id3' back.
An fix is as following, the key is wrap the callback function inside another function to create another closure.
var selectName = ["id1","id2","id3"];
var setOnClickSelect = function (prefix, selectName) {
for(var i = 0; i < selectName.length; i++) {
var selId = selectName[i];
$(selId).onchange = (function (thisId) {
return function() {
$(thisId).value = $(thisId).options[$(thisId).selectedIndex].text;
}
})(selId);
}
};
Ps: there is synyax error for var selectName["id1","id2","id3"], you should use var selectName = ["id1","id2","id3"];

Categories