2

I am still in the process of writing this, but I am now in a slump. This might be a novice question but is there a way to click on the image itself? I was hoping to incorprate it as an e-commerce website [add to cart] feature. However, the alert only appears when I click outside the image, and not on the image itself. Any advices and suggestions will be very much appreciated!

let listProductHTML = document.querySelector('.ProductContainer');

let listProduct = [];

const addProductToHTML = () => {
    listProductHTML.innerHTML = '';
    if(listProduct.length > 0){
        listProduct.forEach(Product => {
            let newProduct = document.createElement('div');
            newProduct.classList.add('iconProduct');
            newProduct.dataset.id = Product.id;
            newProduct.innerHTML = `
                <img alt="Product image"
                src="${Product.image}" width="128" height="128">
                <p>${Product.name}</p>
            `;
            listProductHTML.appendChild(newProduct);
        })
    }
}

listProductHTML.addEventListener('click', (event) => {
    let positionClick = event.target;
    let Product_id = positionClick.dataset.id;
    if(positionClick.classList.contains('iconProduct')){
        let Product_id = positionClick.dataset.id;
        alert(Product_id);
    }

//I am unable to move forward at this point

})


const initApp = () => {
    //get data from json
    fetch('Products.json')
    .then(response => response.json())
    .then(data => {
        listProduct = data;
        addProductToHTML();
    })
}
initApp();
0

2 Answers 2

3

You’re very close. The problem is that your click handler is attached to .ProductContainer, but inside the handler you’re checking the class on event.target.

When you click on the image or the <p>, event.target is that inner element, not the .iconProduct div, so this check fails:

if (positionClick.classList.contains('iconProduct')) {

A simple way to fix this is to walk up to the closest .iconProduct element and use that:

listProductHTML.addEventListener('click', (event) => {
    const productEl = event.target.closest('.iconProduct');
    if (!productEl) return; // click was outside a product

    const productId = productEl.dataset.id;
    alert(productId);
});

Now clicking the image, the name, or any empty space inside the product card will all trigger the alert with the correct data-id.

Sign up to request clarification or add additional context in comments.

2 Comments

I never thought of that! I appreciate you greatly for allowing me to realize where I fell short. It was wrong of me to assume that attaching the click handler to the .ProductContainer div would enable it to identify any element within it.
Happens to all of us. It’s easy to assume the event target will always be the parent, especially when you’re still getting used to how bubbling works. The handler on .ProductContainer does catch the click, but it still reports the exact element you clicked on. Once you know that detail, the rest starts to make a lot more sense.
1

You are indeed successfully clicking on the img element. However, this evaluates to undefined:

let Product_id = positionClick.dataset.id;

And this evaluates to false so the if block doesn't execute:

if(positionClick.classList.contains('iconProduct'))

This is because the dataset.id and the class are added to the div, not to the img.

You can create the img (and the p) the same way you create the div, appending each to their respective containers, to make the code a little cleaner to work with. Then, at least just to demonstrate, you can add the dataset.id and class to the img as well. For example:

const addProductToHTML = () => {
  listProductHTML.innerHTML = '';
  if(listProduct.length > 0){
    listProduct.forEach(Product => {
      let newProduct = document.createElement('div');
      newProduct.classList.add('iconProduct');
      newProduct.dataset.id = Product.id;

      let newImage = document.createElement('img');
      newImage.classList.add('iconProduct'); // <-- here, so the if block can execute in the click handler
      newImage.dataset.id = Product.id;      // <-- here, so the value can be read in the click handler
      newImage.alt = 'Product image';
      newImage.src = Product.image;
      newImage.width = 128;
      newImage.height = 128;

      let newText = document.createElement('p');
      newText.innerText = Product.name;

      newProduct.appendChild(newImage);
      newProduct.appendChild(newText);
      listProductHTML.appendChild(newProduct);
    })
  }
}

Since this adds a class to the image, your styling may change. (We don't have a complete example, so this is mainly speculation.) So there will likely need to be some adjustments made. You could perhaps use a different class on the image and check for that in the click handler, for example.

1 Comment

I appreciate the substantial demonstration! I know this may seem like another novice question, but does that mean that the data-id does not automatically include the elements within the div?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.