I was practicing my array manipulation with a simple dynamic selector using HTML, CSS, and JavaScript when I came across something peculiar. My JS functions seem to be behaving as I want them to, but I get a console error whenever they run. It reads
Uncaught TypeError: Cannot read properties of undefined (reading 'name')
Could someone take a look at my code and see what is happening? Like I said, everything seems to be acting as I would expect, I just cannot figure out why it is throwing this console error, especially because it is clearing reading the object correctly.
let selector = document.querySelector(".selector");
let options = [];
let createItem = (input) => {
let item = {};
item.name = input;
item.id = Math.trunc(Math.random() * 50);
options.push(item);
let el = document.createElement("option");
el.setAttribute("value", input);
el.setAttribute("id", item.id);
el.innerHTML = input;
selector.appendChild(el);
};
createItem("Apple");
createItem("Pear");
createItem("Grape");
createItem("Watermelon");
let addItem = (input) => {
options.push(input);
let latestOption = options[options.length - 1];
createOptions(latestOption);
};
let deleteItem = (input) => {
for (i = 0; i <= options.length; i ) {
let indexedItem = options[i].name;
if (indexedItem === input) {
let el = document.getElementById(options[i].id);
el.remove();
options.splice(i, 1);
}
}
};
let editItem = () => {
for (i = 0; i <= options.length; i ) {
options[i].name = input;
el = document.getElementById(i);
el.setAttribute("value", input);
el.innerHTML = input;
}
};
document.querySelector(".add-btn").addEventListener("click", () => {
let userInput = prompt("What should the new item be called?");
createItem(userInput);
});
document.querySelector(".delete-btn").addEventListener("click", () => {
let userInput = prompt("Which item would you like to delete?");
deleteItem(userInput);
});
document.querySelector(".edit-btn").addEventListener("click", () => {
let selectedItem = prompt("What item would you like to edit?");
let newItemName = prompt("What would you like to rename it as?");
for (i = 0; i <= options.length; i ) {
if (options[i].name === selectedItem) {
options[i].name = newItemName;
let el = document.getElementById(options[i].id);
el.setAttribute("value", options[i].name);
el.innerHTML = options[i].name;
}
}
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options</title>
</head>
<body>
<select title="selector" name="selector" id=""></select>
<button onClick="return false" >Add Item</button>
<button onClick="return false" >Delete Item</button>
<button onClick="return false" >Edit Item</button>
<script src="script.js" defer></script>
</body>
</html>
CodePudding user response:
The problem I see is with the way you defined the for
loop in your function editItem
.
Short answer:
replace for (i = 0; i <= options.length; i )
with for (i = 0; i < options.length; i )
(removing the = in the condition).
Long answer:
As to why its wrong, lets say options
has 6 items in it, your for
starts at i = 0
and is supposed to go on until i = 6
, the problem is there is no options[6] (meaning it has 7 items if you include options[0]) so it cannot read property name
of the non existent options[6]
.
As to why your code doesnt break, it actually does, the function stops working after that, which in your case doesnt cause a problem since the part it was executing was extra anyways.
CodePudding user response:
The issue was you did not use var i in your loop and also your <= should be <
for (var i = 0; i < options.length; i ) {
console.log(i);
if (options[i].name === selectedItem) {
options[i].name = newItemName;
let el = document.getElementById(options[i].id);
el.setAttribute("value", options[i].name);
el.innerHTML = options[i].name;
}
}
full code
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Options</title>
</head>
<body>
<select title="selector" name="selector" id=""></select>
<button onClick="return false" >Add Item</button>
<button onClick="return false" >Delete Item</button>
<button onClick="return false" >Edit Item</button>
<script>
let selector = document.querySelector(".selector");
let options = [];
let createItem = (input) => {
let item = {};
item.name = input;
item.id = Math.trunc(Math.random() * 50);
options.push(item);
let el = document.createElement("option");
el.setAttribute("value", input);
el.setAttribute("id", item.id);
el.innerHTML = input;
selector.appendChild(el);
};
createItem("Apple");
createItem("Pear");
createItem("Grape");
createItem("Watermelon");
let addItem = (input) => {
options.push(input);
let latestOption = options[options.length - 1];
createOptions(latestOption);
};
let deleteItem = (input) => {
for (i = 0; i <= options.length; i ) {
let indexedItem = options[i].name;
if (indexedItem === input) {
let el = document.getElementById(options[i].id);
el.remove();
options.splice(i, 1);
}
}
};
let editItem = () => {
for (i = 0; i <= options.length; i ) {
options[i].name = input;
el = document.getElementById(i);
el.setAttribute("value", input);
el.innerHTML = input;
}
};
document.querySelector(".add-btn").addEventListener("click", () => {
let userInput = prompt("What should the new item be called?");
createItem(userInput);
});
document.querySelector(".delete-btn").addEventListener("click", () => {
let userInput = prompt("Which item would you like to delete?");
deleteItem(userInput);
});
document.querySelector(".edit-btn").addEventListener("click", () => {
let selectedItem = prompt("What item would you like to edit?");
let newItemName = prompt("What would you like to rename it as?");
console.log(options);
for (var i = 0; i < options.length; i ) {
console.log(i);
if (options[i].name === selectedItem) {
options[i].name = newItemName;
let el = document.getElementById(options[i].id);
el.setAttribute("value", options[i].name);
el.innerHTML = options[i].name;
}
}
});
</script>
</body>
</html>