How can I access the index of the clicked item? - javascript

I'm building a shop section for a school project. It works like a gallery: when clicking on an card, a new display opens up with fullscreen info about the product: img, product title, price... I've managed to match the image of the fullscreen display to the one clicked (you won't see the images since it's a relative URL).
Now I need to match the rest of the info.
const miniaturas = document.querySelectorAll(".galeria a");
const modal = document.querySelector(".modal");
const imgModal = document.querySelector(".modal img");
const plantNames = document.querySelectorAll(".galeria h3")
const plantNameModal = document.querySelector(".modal h3");
miniaturas.forEach(function(miniatura){
miniatura.addEventListener("click", function(evento){
evento.preventDefault();
imgModal.setAttribute("src",miniatura.getAttribute("href"));
modal.classList.add("visible");
for(i = 0; i < miniaturas.length; i++){
plantNameModal.innerHTML = plantNames[i].innerHTML;
}
})
})
modal.addEventListener("click", function(){
modal.classList.remove("visible");
})
I don't know how to get the innerHTML via each item's index. I've tried using a for loop and the parameter "miniatura" from the function. CodePen here.

You can get the index of each miniatura as the second argument to the handler function you pass to the forEach. You have access to this index within the body of the addEventListener, as in:
miniaturas.forEach(function(miniatura, index){
miniatura.addEventListener("click", function(evento){
evento.preventDefault();
imgModal.setAttribute("src",miniatura.getAttribute("href"));
modal.classList.add("visible");
// instead of for loop, use `index` to access
// the corresponding elements in the arrays:
plantNameModal.innerHTML = plantNames[index].innerHTML;
})
});
I have forked your Pen.

Related

onclick inside forEach, value parameter only refers to last element

I have a list of anchor in my html, I want to make their href editable.
Everything fine, but the validation step (last onclick) refers to the last anchor instead of the current one
var anchors = document.querySelectorAll('.home-content a');
var col = document.querySelectorAll('.home-content > article');
anchors.forEach((k)=> {
let linkpanel = document.getElementById('link-edit-panel'); //This element is a single div in my html
let linkpanelvalidate = document.getElementById('validate-link'); //the button inside the said div
let editinput = linkpanel.querySelector('input'); //the input inside this div
//For each anchors, I add a button that will let user show the "linkpanel" div to edit the href of this anchor
let editbut = document.createElement('div');
let linktxt = k.href;
editbut.classList.add('edit-but','toremove');
editbut.innerHTML = "<i class='fas fa-link'></i>";
//I put this new element to the current anchor
k.appendChild(editbut);
console.log(k); // this to show me the full list of anchors
/* PROBLEM START HERE */
//click on the "edit" button
editbut.onclick = ()=>{
console.log(k); //Here, it shows the good anchor!
}
//click on the "validate" button
linkpanelvalidate.onclick = ()=>{
console.log(k); //Here, it shows the very last anchor...
}
});
I tried to put the element inside a constant
const ttt = k;
It does not change a thing.
Thank you for your help
We are facing here a classical forEach bubbling misunderstand (and I was blind not to see it)
When the click on the validate button occures, the call is made from the "main" bubble (outside the loop function if you need to picture it) so naturaly, it returns the last occurrence of the loop when we print the value in the console for example.
Solution
There is many solutions, you can store these values in an array to use each of them later
var arr = [];
node.forEach((v)=>{
arr.push(v);
});
Or, you don't want to deal with an array and want to keep it simple, like me, and you create your button during the forEach loop event, like this
node.forEach((v)=>{
let btn = document.createElement('button');
document.body.appendChild(btn);
btn.onclick = ()=> {
console.log(v); //it is the current value, not the last one
//you can create another button here and put his onclick here, the value will still remains etc
}
});

creating a dynamic ul using Javascript

I am trying to create a li item and append the fragment dynamically to ul using only javascript. it is part of a project. I have written a code but every time the console is giving me a different error, the last one was "document is not defined". I need to know what I am doing wrong.
the errors I get on the console was:
1- getAttribute is not a function.
2-document is not defined
3- appendChild is not a function.
4- cannot appendChild to null
const nav_list= document.querySelectorAll("section");
const myUl = document.getElementById("navbar__list");
// create fragment
let nav_fragment = document.createDocumentFragment();
function addItemList(){
for (let i = 0; i < nav_list.length; i++){
let newText= nav_list[i].getAttribute("data-nav");
// creat new li
let newLi= documnet.creatElement("li");
// create new link
let newLink= document.createElement ("a");
// create text node
let textNode = document.creatTextNode ("newText");
// add eventListener
newLink.addEventListener("click", function(){
section[i].scrollIntoView({behavior : "smooth"});
});
newLink.appendChild(textNode);
newLi.appendChild(newLink);
nav_fragment.appendChild(newLi);
}
myUl.appendChild(nav_fragment);
}
// Build menu
addItemList();
** edit: the script tag is added at the bottom of the html right before the .
To be sure that the document is loaded, add your script in a function called when the DOMContentLoaded event is triggered.
window.addEventListener('DOMContentLoaded', (event) => {
... your code here
});

Removing an array element by clicking an HTML <li>

So I'm doing a playlist manager for youtube (using the ytb api) and for the graphic part I'm doing, such as youtube has, a list of every thumbnail there is in a given playlist. I use an HTML 'ul' and add every thumbnail as a 'li'.
Everything is working fine but id like to add a feature so the user could click on one of the thumbnails to remove it from the playlist.
First, let me explain how the important part is coded.
I use an array as a queue to stock every video ID that will be played (this is the playlist) :
var queue = []
And for the thumbnail list I use this function :
function refreshThumbnailsQueue() {
var thumbnailsUl = document.getElementById('thumbnailslist');
while(thumbnailsUl.firstChild) {
thumbnailsUl.removeChild(thumbnailsUl.firstChild );
}
for (var i = 0; i <= queue.length - 1; i++) {
var thumbnail = 'http://img.youtube.com/vi/' + queue[i] + '/maxresdefault.jpg';
var newLi = document.createElement('li');
newLi.className = 'thumbnailLi';
newLi.onclick = function() {
removeFromQueue();
}
var newImg = document.createElement('img');
newImg.className = 'thumbnailImg';
newImg.src = thumbnail;
newLi.appendChild(newImg);
thumbnailsUl.appendChild(newLi);
}
}
So I'm just removing every child the ul has and then filling it with every thumbnail of the video IDs there are in my queue var.
As you can see, there is a removeFromQueue() function called with an onclick event on each li in the code, and this is what I try to code.
Basicaly, if you click the third li, it should remove the third element of my queue var.
If you have any ideas, please let me know. (and BTW sorry for the mistakes English isn't my main language)
Thanks!
Note : I dont want to use jQuery.
If jQuery is an option, you can simply do the following :
$( "li" ).click(function(){
$( this ).remove();
})
As simple as that. If you want more information, I'll update my answer.
You can also visit this page for plain old javascript. Here is the important part :
var elem = document.getElementById("myDiv");
elem.parentNode.removeChild(elem);
As for the index of the li element
When you insert the list item in the DOM, you also set it's ID like this :
function refreshThumbnailsQueue() {
...
for (var i = 0; i <= queue.length - 1; i++) {
...
// Create the li.
var newLi = document.createElement('li');
newLi.id = "song-" + i;
// Create the onclick listening
li.onclick = function(){
// Remove from DOM.
var elem = document.getElementById(this.id);
elem.parentNode.removeChild(elem);
// We keep only the integer (index)
// In this example, '5' cuts away the "song-".
var index = parseInt((this.id+"").substring(5));
// Then, we remove it from the list.
YourProgram.removeIndex(index);
}
...
thumbnailsUl.appendChild(newLi);
}
}
This way, you know what it's index is.
Hope it helps.
Pass the index of the element to remove to the removeFromQueue(), like removeFromQueue(i). Then remove the item from queue.
function removeFromQueue(index) {
queue.splice(index, 1)
refreshThumbnailsQueue()
}

document.getElementsByClassName('name') has a length of 0

I'm trying to retrieve all the elements by class name by using the method getElementsByClassName (no jQuery). Console.log shows the array of the elements, but when I try to get the length of the array, it returns 0.
Saw a previous post with a similar problem that provided a solution of making sure that the element exists. I took this into consideration and fire the function after DOMcreation.
What is going on here?
document.addEventListener("DOMContentLoaded", function(){
// Load current todo items from db
loadTodoItems();
var submitBtn = document.getElementById('submit-btn');
submitBtn.addEventListener('click', addItem);
var removeButton = document.getElementsByClassName("remove-btn");
console.log(removeButton);
console.log(removeButton.length);
}, false)
Another issue to note, is that I am noticing that in chrome Dev Tools when I look at the head tag, I'm seeing that some sort of angular code is being loaded in a script tag in the head, while the content that should be in my head tag are being loaded in the body. I am not using angular for my app.
Edit. Here is my HTML. It is in Jade:
Layout.jade:
doctype html
html(lang='en')
head
meta(charset="utf-8")
title To Do List
link(rel="stylesheet" href="main.css")
body
block content
script(src="main.js")
index.jade:
extends layout
block content
div.container
h1 Get-Er-Done!
form#todo-form(role="form" method="POST" action="/todo")
div.form-group
label(for="addToDo") Add To-Do item:
input#todoItemText.form-control(type="text")
button(type="submit")#submit-btn.btn.btn-default Add
ul#todo-list
Edit. The remove button is for the new todo item. This is called every time the user clicks add to post the new item.
function renderItems(items){
// Before rendering todo-items, clear the existing items in the list
var list = document.getElementById('todo-list');
while(list.hasChildNodes()){
list.removeChild(list.lastChild);
}
// Loop through items
for (var i = 0; i < items.length; i++) {
var el = document.createElement("li");
var removeBtn = document.createElement('button');
var btnText = document.createTextNode('Done');
removeBtn.appendChild(btnText);
removeBtn.className = 'remove-btn';
var newItemText = document.createTextNode(items[i].item);
el.appendChild(newItemText); // Add new content to new div
el.appendChild(removeBtn);
// To-Do list to append to
list.appendChild(el);
}
}
Length is 0 because when you run it there's no element with class remove-btn. You can see the items in the console because getElementsByClassName returns a live HTMLCollection, which is updated when new elements are added.
The problem is that you create the elements with that class in renderItems, which runs after you run getElementsByClassName.
You can fix it using
function renderItems(items){
var list = document.getElementById('todo-list');
while(list.hasChildNodes()){/* ... */}
for (var i = 0; i < items.length; i++) {/* ... */}
// Load current todo items from db
loadTodoItems();
var submitBtn = document.getElementById('submit-btn');
submitBtn.addEventListener('click', addItem);
var removeButton = document.getElementsByClassName("remove-btn");
console.log(removeButton);
console.log(removeButton.length);
}

Need to fix JavaScript so that the array begins at the right position

Hi I have got a jsFiddle to share but never used it before so I hope I did it right..
http://jsfiddle.net/Mayron/3V62C/
I also have these screen shot to better show what I mean:
http://i.imgur.com/NgQk5P2.jpg
I have a table of images in the gallery page and when you click on an image it opens up a frame that shows a blank but much larger image which gains the source of the smaller image you clicked on. That works fine but I have all the images in the gallery listed in an array and the large frame has three buttons on it: previous, close and next. So far the JavaScript code allows you to click next and previous to go through them but it always begins the journey from array[0] and that is no good if you click to view the 6th one in the list from the gallery page first for example.
I hope that makes sense if not then let me know!
The issue is with this line that causes it to begin from the first image:
currentIndex = 0;
How can I change this so that the currentIndex number is the image that the user first clicks on?
There are several ways but that really depends on what you use and how clean you want your code to look. I put a hidden input in each of my divs and retrieve it with JQuery's $("input", $(this).parent()).val();
You could also use Jquery's index. http://api.jquery.com/index/
You could have it in a class and retrieve the class of the clicked item (same idea as having an ID for each image)
On click you can have a selector added in the class attribute then you can use a for-each to count how many images go through before you hit that class in your image order.
You could also do showLarge(this, index#) and set the index that way. I cannot get your JS fiddle to work though.
Do this for every image line:
<td><a href="#" onclick="javaScript:showLarge(this,1);" ><img class="imgBorder" id="image1" src="Media//Gallery//img_1.jpg" alt="Gallery Image 1" /></a>
</td>
This puts onclick in anchor tag and adds an id in the image tag named image1 (1 is the number you put in the anchor tag 2nd param)
In javascript, do:
function showLarge(img, index) {
var largeFrame = document.getElementById("zoomedIn");
largeFrame.style.visibility = 'visible';
var largeImage = document.getElementById("largeImage");
src = document.getElementById("image"+index);
largeImage.src = src.src;
currentImage = index;
}
This gets the image by id according to index clicked
For the next+prev button do:
function changeImage(direction) {
index = parseInt(currentIndex) + parseInt(direction);
if (index < 1) {
index = 1; // or use imgArray.length to rotate round
}
if (index > imgArray.length) {
index = imgArray.length; // or use 0 to rotate round
}
src = document.getElementById("image"+index);
document.getElementById('largeImage').src = src.src;
currentIndex = index;
}
Try something like this:
function findIndex(src) {
for (i = 0; i = imgArray.length; i++)
if (imgArray[i].src == src) {
currentIndex = i;
return;
}
}
function showLarge(img) {
var largeFrame = document.getElementById("zoomedIn");
largeFrame.style.visibility = 'visible';
var largeImage = document.getElementById("largeImage");
largeImage.src = img.src;
findIndex(img.src);
}
It will update the currentIndex value.
I've updated your jsfiddle: http://jsfiddle.net/RN4bN/
But I can't run it, so try directly on your code.
Put an ID on each of them which is the number of it from the array, then when it is clicked change the currentIndex to that.

Categories

Resources