I have created some dynamic divs under 'parent' div and set fetched data from json in these dynamic divs. now I want to use a onclick function to get the index value of these dynamic divs which will be clicked. but don't know how to do.
fetch('https://restcountries.com/v3.1/all')
.then(response => response.json())
.then(json => display(json));
function display(datas) {
let parent = document.getElementById("parent");
datas.forEach(data => {
let div = document.createElement('div');
div.classList.add('list');
div.innerHTML = `
<li>country Name: ${data.name.common}</li>
<li>country Continents: ${data.continents[0]}</li>
<li>Total arae: ${data.area}</li>
<img src="${data.flags.png}" alt="" style="display: none">`
parent.appendChild(div);
})
}
<body>
<div id="parent">
<img src="" alt="">
</div>
<script src="country.js"></script>
</body>
You can get the index of an html element relative to a parent by getting a collection of the elements, convert it to an array, and then get the index in the usual way with the native array indexOf method:
function getIndex(div) {
const parent = document.getElementById("parent");
const collection = Array.from(parent.querySelectorAll(".list"));
const index = collection.indexOf(div);
return index;
}
You can use event delegation to automatically assign a click handler to all .list elements as they are created
const parent = document.getElementById("parent");
parent.addEventListener('click', function (event) {
const item = event.target;
if (item.matches(".list")) {
console.log("index: ", getIndex(item));
}
});
fetch(//...
example:
const parent = document.getElementById("parent");
function getIndex(div) {
const collection = Array.from(parent.querySelectorAll(".list"));
const index = collection.indexOf(div);
return index;
}
parent.addEventListener('click', function (event) {
const item = event.target;
if (item.matches(".list")) {
console.log("index: ", getIndex(item));
}
});
parent.innerHTML = `
<div class="list">A</div>
<div class="list">B</div>
<div class="list">C</div>
<div class="list">D</div>
`;
<div id="parent"></div>
You can achieve index using second argument of forEach iteration function
arr.forEach(function callback(currentValue, index, array) {
//your iterator
}[, thisArg]);
So, part of your code will look like this:
datas.forEach((data, index) => {
Related
Here I have a parent which contains a few children.
I want to display the index (number) of each child on the console by clicking on it.
i tried to use forEach method to detect the clicked child, but when i try to get the index of the child that i clicked it does not work. I tried indexOf() method but it shows an error
let parent = document.querySelector('.parent');
let children = document.querySelectorAll('.child');
children.forEach(child => {
child.onclick = function () {
console.log( /* children.indexOf(child) */ )
// this is the method i tried but it didn't worked
console.log( /*here i want to display the index of the clicked child */ );
}
});
<div class="parent">
<div class="child">a</div>
<div class="child">b</div>
<div class="child">c</div>
<div class="child">d</div>
</div>
You just need to convert children from a NodeList into an array (using Array.from) to use indexOf:
let parent = document.querySelector('.parent');
let children = document.querySelectorAll('.child');
children.forEach(child => {
child.onclick = function () {
console.log(Array.from(children).indexOf(child));
}
});
<div class="parent">
<div class="child">a</div>
<div class="child">b</div>
<div class="child">c</div>
<div class="child">d</div>
</div>
I have 3 buttons and 3 content.
When I click on any button, I want to show the content of that button. When I click on the other button, I want to show the content of that button and delete the content of the other button.
I managed to do this with simple logic, but how can I do it with a for loop?
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent[0].addEventListener('click',
function() {
content[1].classList.remove('show-modal');
content[2].classList.remove('show-modal');
content[0].classList.add('show-modal');
}
)
btnsOpenContent[1].addEventListener('click',
function() {
content[0].classList.remove('show-modal');
content[2].classList.remove('show-modal');
content[1].classList.add('show-modal');
}
)
btnsOpenContent[2].addEventListener('click',
function() {
content[0].classList.remove('show-modal');
content[1].classList.remove('show-modal');
content[2].classList.add('show-modal');
}
)
Use simple forEach loops along with classList.toggle(className: string, force: boolean):
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((btn, index) => {
btn.addEventListener('click', () => {
content.forEach((cnt, idx) => cnt.classList.toggle('show-modal', idx === index))
})
})
I strongly suggest to delegate, which means instead of adding a listener to each and every button, you instead capitalize on the bubbling mechanism of events, listening to clicks instead on just one common ancestor element of all buttons. Then inside the click handler you implement a guard that checks if the click actually came from a button.
Note I added hidden to the last two content to start with content 1
You can also add active to the button that was clicked and remove from the siblings
NOTE: This code does not change no matter how many buttons as long as there is a matching number of content divs.
Also note there is no need for a class to show and hide
Lastly note I implemented your close too
const nav = document.getElementById("nav");
const contents = document.getElementById("contents");
const buttons = nav.querySelectorAll("#nav .show-content");
const contentDivs = contents.querySelectorAll("div.content")
nav.addEventListener("click", e => {
const tgt = e.target; // what was clicked?
if (!tgt.matches(".show-content")) return; // not a button
buttons.forEach((but,i) => contentDivs[i].hidden = but !== tgt); // hide if not the content that belongs to button
})
contents.addEventListener("click", e => {
const tgt = e.target;
if (!tgt.matches(".close")) return; // not a button
tgt.closest("div").hidden = true; // hide the div the close button is in
})
<div id="nav">
<button class="show-content">Show 1</button>
<button class="show-content">Show 2</button>
<button class="show-content">Show 3</button>
</div>
<div id="contents">
<div class="content">Content 1 <button class="close">X</button></div>
<div class="content" hidden>Content 2 <button class="close">X</button></div>
<div class="content" hidden>Content 3 <button class="close">X</button></div>
</div>
The most important thing to note is that querySelectorAll does not return an array, and instead returns a NodeList. ES6 has introduced a handy method that is intended for this exact purpose NodeList.prototype.forEach().
The easiest approach in my experience to create tabbed content is to add some sort of identifier for each ".content" tab on the button triggering the event. the "data" attribute is often used for this, however, there several other options.
An example of your button html using the data attribute would look like the following:
<button class="show-content" data-content="content1">Show content 1</button>
<button class="show-content" data-content="content2">Show content 2</button>
<button class="show-content" data-content="content3">Show content 3</button>
For this technique your content HTML would need an id that matches the identifier used on your buttons:
<div class="content" id="content1">Some content for 1</div>
<div class="content" id="content2">Some content for 2</div>
<div class="content" id="content3">Some content for 3</div>
And the corresponding javascript would look similar to below:
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((button) => {
button.addEventListener('click', (event) => {
let contentId = event.target.getAttribute('data-content');
let targetContent = document.getElementById(contentId);
// Hide all contents
document.querySelectorAll('content').forEach((element) => {
element.classList.remove('show-modal');
})
// Show selected content
targetContent.classList.add('show-modal');
});
});
This code can even be shortened:
const btnsOpenContent = document.querySelectorAll('.show-content');
btnsOpenContent.forEach((button) => {
button.addEventListener('click', (event) => {
let contentId = event.target.dataset.content;
let targetContent = document.getElementById(contentId);
document.querySelectorAll('content').forEach((element) => {
element.classList.toggle('show-modal', contentId === element.id);
})
});
});
Maybe something like this?
const content = document.querySelectorAll('.content');
const contentClose = document.querySelector('.close-content');
const btnsOpenContent = document.querySelectorAll('.show-content');
for (let i = 0; i <= 2; i++) {
btnsOpenContent[i].onclick = () => {
for (let j = 0; j <= 2; j++) {
i == j ? content[j].classList.add('show-modal') : content[j].classList.remove('show-modal');
}
};
}
I filtered the HTML class elements using
elements = document.querySelectorAll("div._3arMG")
Now that I have a list of HTML elements I need, I want the CSS Selector/Path for each HTML element in the elements list.
CSS Selector when I right click on HTML ELement in Console -> Copy -> Copy Selector -> #root > div._3arMG
Please suggest how do I get this CSS Selector using Javascript?
You could use either Element.querySelector() or Element.querySelectorAll(), choose which suits your case
const elements = document.querySelectorAll("div._3arMG")
for (const element of elements){
const subElements = element.querySelectorAll('.someclass')
// or
const subElement = element.querySelector('.someclass')
// ...
}
Reference
Element.querySelector()
Element.querySelectorAll()
You can do it recursivly with parentElement for the next node above. Getting for every node the tagname by nodeName, the classes with classList and the id.
Edited: Because Id's are unique I stop if an id occurs in the path.
let elements = document.querySelectorAll("div._3arMG");
let result = [];
elements.forEach(el => {
result.push(getPath(el));
})
console.log(result);
function getPath(el) {
let act = el.nodeName;
el.classList.forEach(cl => act += '.' + cl);
if (el.id) act += '#' + el.id;
if (!el.id && el.parentElement) {
let res = getPath(el.parentElement);
res.push(act);
return res;
} else {
return [act];
}
}
<div class='Wrapper'>
<div class="_3arMG myClass">
Test
</div>
<table id='myTable'>
<tr>
<td>
<div class="_3arMG">My span</div>
</td>
</tr>
</table>
</div>
I'm trying to get the attributes of an element's children ... something like this:
<div id="mydivs">
<div name="mydiv1" name="format1">
</div>
<div name="mydiv2" format="format2">
</div>
</div>
var parent = document.getElementById('mydivs');
var children = parent.childNodes;
for (let child of children) {
console.log(child)
console.log(child.attributes)
}
child.atrribute returns undefined
JsFiddle: https://jsfiddle.net/8tdac5fn/
Edit: Another option would be to use children instead of childNodes
var parent = document.getElementById('mydivs');
var children = parent.children;
parent.childNodes will include text nodes and that is why you are getting undefined, so you can first check if nodeType is 1 which is Node.ELEMENT_NODE.
Then you could use spread syntax in array to create array of attribute nodes so you can use forEach loop.
var parent = document.getElementById('mydivs');
var children = parent.childNodes;
for (let child of children) {
if (child.nodeType === 1) {
[...child.attributes].forEach(({name,value}) => {
console.log(`${name} - ${value}`);
})
}
}
<div id="mydivs">
<div name="mydiv1" name="format1"></div>
<div name="mydiv2" format="format2"></div>
</div>
I want to add a class to an element that shares the same data attribute value using vanilla JS. The class is added on mouseenter.
My current setup only applies the class on hover to the first element and ignores the rest.
let section = document.querySelector('.section');
let links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index) {
link.addEventListener('mouseenter', function() {
triggerVal = this.dataset.triggerValue;
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
});
});
<ul class="links">
<li>
<a data-trigger-value="red" href="#">Red</a>
</li>
<li>
<a data-trigger-value="yellow" href="#">Yellow</a>
</li>
<li>
<a data-trigger-value="blue" href="#">Blue</a>
</li>
</ul>
<div class="wrapper">
<div class="section" data-linked-value="red">
<h2>Red</h2>
</div>
<div class="section" data-linked-value="yellow">
<h2>Yellow</h2>
</div>
<div class="section" data-linked-value="blue">
<h2>Blue</h2>
</div>
</div>
Here's a Codepen: https://codepen.io/abbasarezoo/pen/7378e190ed6ad117faca968b634520b0
I've got a feeling it's to do with the .section element but I've tried a few things and nothing seems to give me what I need.
Any suggestions as to what I need to do to get the rest of the elements working?
You need to change two things:
First, get all sections:
const section = document.querySelectorAll('.section');
Then, inside your handler, you need to iterate over the NodeList returned by querySelectorAll():
for (const section of sections) {
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
}
This is your new JS:
const sections = document.querySelectorAll('.section');
const links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index){
link.addEventListener('mouseenter', (e) => {
triggerVal = e.target.dataset.triggerValue;
for (const section of sections) {
linkedVal = section.dataset.linkedValue;
if (linkedVal === triggerVal) {
section.classList.add('is-active');
} else {
section.classList.remove('is-active');
}
}
});
});
You need to use document.querySelectorAll for sections and then forEach. And use toggle instead of add/remove for this case. https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
let sections = document.querySelectorAll('.section');
let links = document.querySelectorAll('.links a');
let triggerVal;
let linkedVal;
links.forEach(function(link, index) {
link.addEventListener('mouseenter', function() {
triggerVal = this.dataset.triggerValue;
sections.forEach(
section => section.classList.toggle(
'is-active',
section.dataset.linkedValue === triggerValue
)
);
});
});