I'm trying to create a basic inventory in JavaScript. I would like to add items to an array which would be the player inventory. What's inside the inventory array would be displayed on multiple divs (one per item). I've already starting experiencing and getting the basics to work, but I wanted to elaborate a bit and create buttons per each item in the item list so as to later use the buttons to add the items to the inventory.
I started by selecting my HTML divs and creating a basic itemList array and a playerInv empty array:
const container = document.querySelector('.container');
const inventory = document.querySelector('.inventory');
const invItem = document.querySelectorAll('.grid-item');
const itemList = [{
id: 1,
name: "Coke Bottle",
quality: "usable"
},
{
id: 6,
name: "Pair of Jeans",
quality: "pants"
}
]
// Player inventory, is by default empty.
const playerInv = []
const itemBtnContainer = document.createElement("div");
itemBtnContainer.classList.add("item-add-btn-ctn");
container.appendChild(itemBtnContainer);
let itemBtn;
function addItemBtn() {
itemList.map((item) => {
itemBtn = document.createElement("button");
itemBtn.classList.add("item-add-btn");
itemBtnContainer.appendChild(itemBtn);
itemBtn.innerText = item.name;
})
}
addItemBtn();
<div >
CONT
<div >
<div >
<div >Your inventory</div>
<div >
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
<div ></div>
</div>
</div>
<div ></div>
</div>
</div>
So, in order to create my buttons my idea was to loop through my itemList array with map. Each itemList item would create a button with a class of "item-add-btn" and inside the button would be displayed the name of the item.
Visually, it works. I have 6 buttons because of the 6 itemList items, but because I created the buttons in my loop I can't access them outside of the loop. Indeed, if I console.log(itemBtn) it returns 'undefined', so I can't create an onClick function with addEventListener in order to later write my addItemToInv() function. I understand why it doesn't work, but I really don't know what else I can do to get to the same results AND be able to use my buttons.
I don't even know what else I can try since it's a very specific need.
CodePudding user response:
First you should be using for .. of or Array.prototype.forEach because .map builds a new array which you are discarding.
Second, make a createButton function that returns the button so you can store it to a value -
function createButton(item) {
const itemBtn = document.createElement("button")
itemBtn.type = "button"
itemBtn.classList.add("item-add-btn")
itemBtn.innerText = item.name
return itemBtn
}
Now you can loop -
for (const item of itemList) {
// create button reference
const button = createButton(item)
// add click handler
button.addEventListener("click", ...)
// append button to container
itemBtnContainer.appendChild(button)
}
Here's a full working demo -
const inventory = []
const itemList = [{id:1,name: "Coke Bottle",quality: "usable"},{id: 6,name: "Pair of Jeans",quality: "pants"}]
function renderInventory() {
document
.querySelector("#inventory")
.textContent = `inventory: ${JSON.stringify(inventory, null, 2)}`
}
function createButton(text, onClick) {
const e = document.createElement("button")
e.type = "button"
e.textContent = text
e.addEventListener("click", onClick)
return e
}
function addToInv(item) {
return event => {
inventory.push(item)
renderInventory()
}
}
renderInventory()
for (const item of itemList)
document.body.appendChild(createButton(item.name, addToInv(item)))
#inventory {
padding: 1rem;
background-color: #eee;
font-family: monospace;
}
<pre id="inventory"></pre>
CodePudding user response:
Just add your listener in your loop no ?
itemBtn.addEventListener('click', function(event){
const elem = event.target;
});
Why global itemBtn ?
CodePudding user response:
In your map, you never return the itemBtn that you create. If you modify your code just a little as in the following, you should get a list of button elements you can use.
function addItemBtn() {
return itemList.map((item) => {
const itemBtn = document.createElement("button");
itemBtn.classList.add("item-add-btn");
itemBtnContainer.appendChild(itemBtn);
itemBtn.innerText = item.name;
return itemBtn;
});
}
const buttons = addItemBtn(); // List of button elements to do with what you will
I do want to mention that once you define your addItemToInv function, you could do something like the following:
function addItemToInv(item) {
// Logic to add item to inventory here
}
function addItemBtn() {
return itemList.map((item) => {
const itemBtn = document.createElement("button");
itemBtn.classList.add("item-add-btn");
itemBtnContainer.appendChild(itemBtn);
itemBtn.innerText = item.name;
itemBtn.addEventListener("click", () => addItemToInv(item));
return itemBtn;
});
}
CodePudding user response:
It is unclear why you would need to access the buttons outside the loop. You are clearly using map, so return the button and you now have an array of buttons.
let itemBtns;
function addItemBtn() {
itemBtns = itemList.map((item) => {
itemBtn = document.createElement("button");
itemBtn.classList.add("item-add-btn");
itemBtnContainer.appendChild(itemBtn);
itemBtn.innerText = item.name;
return itemBtn;
});
}
But you probably just want to know when a button is clicked, so you should just add the event listener to the button when you create it.
function addItemBtn() {
itemList.forEach((item) => {
itemBtn = document.createElement("button");
itemBtn.classList.add("item-add-btn");
itemBtnContainer.appendChild(itemBtn);
itemBtn.innerText = item.name;
itemBtn.type = "button";
itemBtn.addEventListener("click", () => {
console.log(item);
});
});
}
