Access array from addEventListener - javascript

I have added a addEventListener to button click event. After clicking button I want to delete global array data.
Now i am not able to pass the array with this.arrayname, I'm trying below code. It's deleting the data but not updating the parent array .
Please let me know how to pass parameter to addEventListener or how to call function from addEventListener, so that I can update the array .
this.array = deletejson;
btn.addEventListener('click', function(event) { for (var i = 0; i < deletejson.length; i++) {
var cur = deletejson[i];
if (cur.keywordname == (document.getElementById("keywordname") as HTMLTextAreaElement).value) {
deletejson.splice(i, 1);
break;
}}});
complete code:
export class ExtractionConfigurationComponent implements OnInit {
array = [];
constructor(private adminWorkflowService:AdminWorkflowService) { }
ngOnInit() {}
addedkeywords(data: any){
btn.addEventListener('click', (event) => {
//var x = document.getElementById(this.id);
//x.style.display='none';
for (var i = 0; i < this.array.length; i++) {
var cur = this.array[i];
if (cur.keywordname == (document.getElementById("keywordname") as HTMLTextAreaElement).value) {
this.array.splice(i, 1);
break;
console.log("after deleting data" +this.array);
console.log("after deleting data" +JSON.stringify(this.array));}}});}

Assuming that the array is stored as this.array (it's unclear in what context you are doing this), then you need access to the lexical this in your event listener's callback. This can be done by using arrow functions:
btn.addEventListener('click', (event) => {
for (var i = 0; i < this.array.length; i++) {
var cur = this.array[i];
if (cur.keywordname == (document.getElementById("keywordname") as HTMLTextAreaElement).value) {
this.array.splice(i, 1);
break;
}
}
});

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>

Value being undefined? [duplicate]

I'm appending onclick events to elements that I'm creating dynamically. I'm using the code below, this is the important part only.
Test.prototype.Show= function (contents) {
for (i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
}
}
First it says that it's undefined. Then I changed and added:
var content = content[i];
menulink.onclick = function () { return that.ClickContent.apply(that, [content]); };
What is happening now is that it always append the last element to all onclick events( aka elements). What I'm doing wrong here?
It's a classical problem. When the callback is called, the loop is finished so the value of i is content.length.
Use this for example :
Test.prototype.Show= function (contents) {
for (var i = 0; i < contents.length; i++) { // no need to have <= and -1
(function(i){ // creates a new variable i
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function () { return that.ClickContent.apply(that, [contents[i]]); };
})(i);
}
}
This immediately called function creates a scope for a new variable i, whose value is thus protected.
Better still, separate the code making the handler into a function, both for clarity and to avoid creating and throwing away builder functions unnecessarily:
Test.prototype.Show = function (contents) {
for (var i = 0; i <= contents.length - 1; i++) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = makeHandler(i);
}
function makeHandler(index) {
return function () {
return that.ClickContent.apply(that, [contents[index]]);
};
}
};
A way to avoid this problem altogether, if you don't need compatibility with IE8, is to introduce a scope with forEach, instead of using a for loop:
Test.prototype.Show = function (contents) {
contents.forEach(function(content) {
var menulink = document.createElement('a');
menulink.href = "javascript:;";
menulink.onclick = function() {
return that.ClickContent.call(that, content);
};
});
}

Binding event listeners multiple times in JavaScript

I'm trying to get the directory list, level by level, using JavaScript.
I have this paths array as input.
var _paths = [];
_paths.push("meta1/meta2/test/home/myself/hi.jpg");
_paths.push("meta1/meta2/test/home/myself/hi1.jpg");
_paths.push("meta1/meta2/test/home/myself/hi2.jpg");
_paths.push("meta1/meta2/test/work/she/100.jpg");
_paths.push("meta1/meta2/test/work/she/110.jpg");
_paths.push("meta1/meta2/test/work/she/120.jpg");
_paths.push("meta1/meta2/test/work/hard/soft/she/120.jpg");
_paths.push("meta1/meta2/test/work/hard/soft/she/121.jpg");
_paths.push("meta1/meta2/test/work/she/220.jpg");
and I want to have a "test" as output, which will be clickable. After click "test", it should be replaced by "home" and "work". After click on "home" - "myself", on "work" - "hard" and "she".
I wrote this:
CodepenCode
and it works only once, only when clicking on "test".
Simply rebind the listeners after the directories have been drawn. You bind them only once, thus they work only once.
Wrap the binding function into a named function:
function bindListeners(){
$('.sub').click(function() {
word = $(this).text();
filteredArr = findString(_paths, word);
drawList(filteredArr, word);
});
}
And call it at the end of drawList:
var drawList = function (paths, word) {
var folders = getFolders(paths, word);
if (folders.length > 0) {
$('.canvas').html('');
for (i = 0; i < folders.length; i++) {
$('.canvas').append("<div class='sub'>" + folders[i] + "</div><br />");
}
}
bindListeners();
}
Demo.
If anyone is curious about building out the data structure:
(function iteratePaths() {
var dirs = [];
for(var i = 0; i < _paths.length; i++) {
buildDirectories(dirs, _paths[i].split('/'));
}
})();
function findDir(dir, obj) {
for(var i = 0; i < dir.length; i++) {
if(dir[i].name === obj.name) {
return dir[i];
}
}
return undefined;
}
function buildDirectories(dir, subs) {
if(subs.length === 0) return;
var obj = {name: subs.shift(), dirs: []};
var existingDir = findDir(dir, obj);
if(!existingDir) {
dir.push(obj);
}
buildDirectories((existingDir || obj).dirs, subs);
}

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

Can not call method of undefined

I'm trying to develop a javascript object that creates a menu in html.
The function receives an object as an argument. Among the object elements is a function that should be executed in an event handler called from a method of my object.
Here is my code :
Menu = function(config) {
var j = 0;
this.config = config;
this.make = function() {
for (i = 0; i < this.config.items.length; i++) {
var vid = document.createElement("div");
vid.className = this.config.cls;
vid.id += i;
document.body.appendChild(vid);
var txt = document.createTextNode(this.config.items[i]);
var pp = document.createElement("p");
pp.appendChild(txt);
vid.appendChild(pp);
}
document.addEventListener("keydown", this.scrolldown, false);
document.onkeydown = function(e) {
var keyCode = e.keyCode;
alert("functional");
if (keyCode == 40) {
alert("You hit key down");
var et = document.getElementById(j);
this.config.trait1(et);
j = j + 1;
} else {
alert("no");
}
}
};
return this;
};
when I call the function make after instantiating the object I have my elements created but my event isn't handled because of :
Uncaught TypeError: Cannot call method 'trait1' of undefined .
Can anyone help me? I saw many answers of the same question but none of the suggested solutions worked.
this inside the Menu function is not the same as this inside the onkeydown function.
Store the value of this in another variable and use that.
Menu = function () {
var myMenu = this; // I'm assuming that you will be calling `new Menu()`
document.onkeydown = function () {
myMenu.config.etc.etc.etc
}
}

Categories