Axel
Axel

Reputation: 5111

How to fire an event only after element is loaded?

I am creating a simple tag system.

I am also adding a functionality of removing arrays. However, I ran into a problem. Whenever I launch an event listener to delete the item, I am getting this error: Uncaught TypeError: Cannot set property 'onclick' of undefined.

I know I am getting this error because the element is not visible/created in the DOM. Since, the tags appear only after a button click, how can I launch an event only after tags element is loaded?

Here's the code:

const tagsInput = document.getElementById('tagsField');
const tagsBtn = document.getElementById('getTags');
const ul = document.getElementById('ul');
let tagsRemover = document.getElementsByClassName('removeTag');
let tagsElemnts = document.getElementsByClassName('tags');
let tagsContainer = [];

let getTags = () => {
	if(tagsInput.value) {
		tagsInput.classList.remove('hasError');
		let tags = tagsInput.value.split(",");
		let pure_tags = tags.map((x) => x.trim());
		tagsContainer = [...pure_tags];
	} else {
		tagsInput.classList.add('hasError');
	}
}

let showTags = () => {
	if(tagsContainer.length > 0) {
		tagsContainer.forEach((element) => {
			let container = document.createElement('LI');
			container.innerHTML = '<span class="removeTag">&#10006;&nbsp;</span>'+element;
			container.className = 'tags';
			ul.appendChild(container);
		});
		tagsContainer = [];
		tagsInput.value = '';
	}
}

tagsInput.addEventListener('input', getTags);
tagsBtn.addEventListener('click', showTags);

// I am doing i < 2 just for debug purpose
for(let i = 0; i < 2; i = i + 1) {
	tagsRemover[i].onclick = () => {
		tagsElemnts[i].style.display = 'none';
	}
}
label {
  font-size: 24px;
  font-family: Roboto;
}
input[type=button] {
  font-size: 18px;
  border: none;
  color: #fff;
  padding: 0.6em 1.5em;
  border-radius: 2em;
  background: #ff4040;
  cursor: pointer;
  outline: none;
  margin-left: 0.5em;
}
input[type=text] {
  font-size: 1.4em;
  padding: 0.4em 0.7em;
  outline: none;
  border: 2px solid #c2c2c2;
  transition: all 150ms ease-in-out;
}
input[type=text]:focus {
  border-color: #0095ff;
}
.tags {
  list-style: none;
  background: #03A9F4;
  color: #fff;
  font-family: Roboto, sans-serif;
  padding: 0.5em 1em;
  border-radius: 10em;
  text-align: center;
  display: inline;
  font-size: 14px;
  margin-left: 0.5em;
}
.removeTag {
  color: #fff;
  display: inline;
  cursor: pointer;
}
.hasError {
  border-color: #ff0023 !important;
}
<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Get Tags</title>
</head>
<body>
	<label for="tags">Tags: </label>
	<input type="text" id="tagsField">&nbsp;
	<input type="button" value="Get Tags" id="getTags">
	<br>
	<br>
	<ul id="ul"></ul>
</body>
</html>

Upvotes: 2

Views: 221

Answers (1)

Vikasdeep Singh
Vikasdeep Singh

Reputation: 21766

Add a check at tagsRemover[i] and error will be gone.

Current code:

tagsRemover[i].onclick = () => {
  tagsElemnts[i].style.display = 'none';
}

Change to:

  if (tagsRemover[i]) {
    tagsRemover[i].onclick = () => {
      tagsElemnts[i].style.display = 'none';
    }
  }

Below is working code snippet:

const tagsInput = document.getElementById('tagsField');
const tagsBtn = document.getElementById('getTags');
const ul = document.getElementById('ul');
let tagsRemover = document.getElementsByClassName('removeTag');
let tagsElemnts = document.getElementsByClassName('tags');
let tagsContainer = [];

let getTags = () => {
  if (tagsInput.value) {
    tagsInput.classList.remove('hasError');
    let tags = tagsInput.value.split(",");
    let pure_tags = tags.map((x) => x.trim());
    tagsContainer = [...pure_tags];
  } else {
    tagsInput.classList.add('hasError');
  }
}

let showTags = () => {
  if (tagsContainer.length > 0) {
    tagsContainer.forEach((element) => {
      let container = document.createElement('LI');
      container.innerHTML = '<span class="removeTag">&#10006;&nbsp;</span>' + element;
      container.className = 'tags';
      ul.appendChild(container);
    });
    tagsContainer = [];
    tagsInput.value = '';
  }
}

tagsInput.addEventListener('input', getTags);
tagsBtn.addEventListener('click', showTags);

// I am doing i < 2 just for debug purpose
for (let i = 0; i < 2; i = i + 1) {
  if (tagsRemover[i]) {
    tagsRemover[i].onclick = () => {
      tagsElemnts[i].style.display = 'none';
    }
  }
}
label {
  font-size: 24px;
  font-family: Roboto;
}

input[type=button] {
  font-size: 18px;
  border: none;
  color: #fff;
  padding: 0.6em 1.5em;
  border-radius: 2em;
  background: #ff4040;
  cursor: pointer;
  outline: none;
  margin-left: 0.5em;
}

input[type=text] {
  font-size: 1.4em;
  padding: 0.4em 0.7em;
  outline: none;
  border: 2px solid #c2c2c2;
  transition: all 150ms ease-in-out;
}

input[type=text]:focus {
  border-color: #0095ff;
}

.tags {
  list-style: none;
  background: #03A9F4;
  color: #fff;
  font-family: Roboto, sans-serif;
  padding: 0.5em 1em;
  border-radius: 10em;
  text-align: center;
  display: inline;
  font-size: 14px;
  margin-left: 0.5em;
}

.removeTag {
  color: #fff;
  display: inline;
  cursor: pointer;
}

.hasError {
  border-color: #ff0023 !important;
}
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Get Tags</title>
</head>

<body>
  <label for="tags">Tags: </label>
  <input type="text" id="tagsField">&nbsp;
  <input type="button" value="Get Tags" id="getTags">
  <br>
  <br>
  <ul id="ul"></ul>
</body>

</html>

Upvotes: 2

Related Questions