js asynchronous problem just work first neighbor - javascript

I want click neighbor and change the main country. I click first card ok no problem. but other cards not working. This is my code:
function renderNeighbors(data) {
let html = "";
for (let country of data) {
html += `
<div class="col-2 mt-2">
<div class="card click-change">
<img src="${country.flags.png}" class="card-img-top">
<div class="card-body">
<h6 class="card-title neighborName">${country.name.common}</h6>
</div>
</div>
</div>
`;
}
document.querySelector("#neighbors").innerHTML = html;
document.querySelector(".click-change").addEventListener("click", () => {
let neighborName = document.querySelector(".neighborName").innerHTML;
getCountry(neighborName);
})
}
in photos I point red circle in my browser. that's only working card.
enter image description here
I want to click neighbors and change main country. but only work for first neighbor
here is my all html code https://pastebin.com/Sb9XWZhy

here, the problem you are setting your event only for the first chosen element that matches your locator .
querySelector return only the first element that matches.
you should add the event for all of your nodes that match the locator.
It's supposed that querySelectorAll returns an array that you can loop over it like that.
for (let element of document.querySelectorAll(".click-change")){
element.addEventListener("click", () => {
let neighborName =
element.children[1].children[0].innerHTML;//here I'm accessing the neighbourName from the element itself, I'm not sure about the indices but you can handle them yourself
getCountry(neighborName);
})
}

Related

How to close template modals in handlebars?

I created an app that receives articles from news API. Each article is displayed in a card, which has a button "Open Modal".
This button opens a modal with the unique information that pertains to each respective article.
However, I am unable to close the modal once it's opened. I suspect it's because the modal is stuck in this state: modals.forEach((modal, index) => {modal.classList.toggle('open', index === openIndex);
Here is my current code:
{{!-- #each article --}}
<div class="row">
{{#each articles}}
<div class="col-12-sm col-6-md col-3-lg">
<div class="card m-2">
<div class="card-body">
<h5 class="card-title">{{title}}</h5>
<p class="card-text">{{description}}</p>
</div>
<img class="card-image" src="{{urlToImage}}" alt="Card image cap">
<button data-open-modal="{{#index}}">Open Modal</button>
</div>
</div>
{{/each}}
</div>
</div>
{{#each articles}}
<!-- The Modal -->
<div class="modal closed" id="Modal_{{#index}}">
<!-- Modal content -->
<div class="modal-content">
<span id="spm" class="close" >×</span>
<h2>{{title}}</h2>
<img src="{{urlToImage}}" alt="">
<p>{{content}}</p>
</div>
</div>
{{/each}}
<script>
//Store all modals and modal buttons in variables
const openModalButtons = document.querySelectorAll('[data-open-modal]');
const modals = document.querySelectorAll('.modal');
//Loop through all modal buttons and assign handler to each
openModalButtons.forEach(openModalButton => {
openModalButton.addEventListener('click', (event) => {
//Get index value from number clicked
const openIndex = Number(event.target.dataset.openModal); //Access dataset attribute to read and write
//Loop over each modal.
//Set modal class to open if index is equal to wanted index
modals.forEach((modal, index) => {
modal.classList.toggle('open', index === openIndex);
modal.classList.toggle('closed', index !== openIndex);
});
});
});
</script>
And here is what I tried adding to my script: (It gave no error but did nothing)
const span = document.querySelectorAll('.close');
let spanArr = Array.prototype.slice.call(span);
spanArr.forEach(spanArr => {
spanArr.addEventListener('click', (event) => {
const closeIndex = Number(event.target.dataset.closeModal);
spanArr[closeIndex].forEach(span => {
span.onclick = function() {
modal.style.display = "none";
}
});
});
});
I also tried adding event listeners to the spans, but I was unable to make it work. I am a beginner and this is my first time using handlebars, so thank you for any insight!
There are some issues here that perhaps are due to absent explanations on my part in https://stackoverflow.com/a/73738690/3397771.
First, the reason that const openIndex = Number(event.target.dataset.openModal); works is because there is a data-attribute called data-open-modal defined on each "open" button. It is that data-attribute that we are referencing with dataset.openModal and the value we will get back is the value on the right-hand-side of the equal sign in the attribute, the {{#index}} part.
However, the data-attribute approach is probably excessively complicated for our purposes here. We could, alternatively, use the index obtained in the forEach loop we use to iteratively add our click event listeners.
Next, there is no need for the spanArr[closeIndex].forEach(... loop inside our click handler. spanArr - despite its name - is not an arr(ay); it is a single span element.
The updated code becomes:
const span = document.querySelectorAll('.close');
span.forEach((spanArr, index) => {
spanArr.addEventListener('click', () => {
modals[index].style.display = "none";
});
});
Note: I have left the names of the variables as I found them, but they could and should be improved. For example, span does not communicate what purpose of these elements is or, for that matter, that it is a collection. closeButtons would be a better name. In fact, elements that behave like buttons should use the <button> element, not <span>, so as to be semantically correct and accessible.
I have created a new fiddle.

Target all elements in a list excepted clicked

I've been playing around with jQuery for ages but am finally trying to learn clean Vanilla JS.
I have a list of elements:
<div id="seriesList" class="seriesList rollable">
<div class="seriesLink" series="7">
<h3 class="name">Carrow Road</h3><p class="location">Norwich</p>
</div>
<div class="seriesLink" series="6">
<h3 class="name">White Heart Lane</h3><p class="location">London</p>
</div>
<div class="seriesLink" series="5">
<h3 class="name">Parc des Princes</h3><p class="location">Paris</p>
</div>
</div
I'm toggling a series of GSAP animation after clicking one of the .seriesLink. The first one i'm trying to achieve is making every elements exept the one clicked disapear.
i.e: I click on #carrow-road — #white-lane and #parc-des-princes would disapear.
I have this:
document.querySelectorAll(".seriesLink").forEach(item => {
item.addEventListener('click', event => {
// ForEach.Not ?
document.getElementById("seriesList").classList.toggle("rollable");
document.getElementById("home").classList.add("scrollable");
document.getElementById("rightPanel").classList.remove("scrollable");
tlOpenSeries.play();
})
})
The "class" system in Javascript is getting me lost, as I don't seem to be able to target my elements successfully.
I can't find a way to "reproduce" the each.not jquery provides. Any idea? Shall I add a class first to the clicked element and then target all elements without this "active" class? Is there a shortcut?
Many thanks
To accomplish that in vanilla JS you have to loop through the elements and check if the current element is not the clicked element.
Demo:
var divs = document.querySelectorAll(".seriesLink");
divs.forEach(item => {
item.addEventListener('click', event => {
for(var i = 0; i < divs.length; i++){
if(event.currentTarget != divs[i]){ // check here
divs[i].style.display = "none";
}
}
//.......
//.......
});
});
<div id="seriesList" class="seriesList rollable">
<div class="seriesLink" series="7">
<h3 class="name">Carrow Road</h3><p class="location">Norwich</p>
</div>
<div class="seriesLink" series="6">
<h3 class="name">White Heart Lane</h3><p class="location">London</p>
</div>
<div class="seriesLink" series="5">
<h3 class="name">Parc des Princes</h3><p class="location">Paris</p>
</div>
</div>
you can use filter:
const seriesLinks = document.querySelectorAll(".seriesLink");
seriesLinks.forEach(item => {
item.addEventListener('click', event => {
seriesLinks
.filter(i => i != item)
.forEach(i => // your logic... //);
//... rest of your code ... //
})
})
but anymay i think that a good practice is to split the code to simple little functions, for example hideAllExceptCurrent (allElemArray, currentElem), hideAllToggleCurrent (allElemArray, currentElem)

Html selector returns an html collection and I don't know how to get to the element I need to make changes on

I have 2 divs: 1 on the left half of the page (A), one on the right (B). When hovering over a certain element of the right section, I want something to be displayed over the left one.
I did this using the following approach:
<div className="A">
<div className="hidden-div1">DIV 1</div>
<div className="hidden-div2">DIV 2</div>
<div className="hidden-div3">DIV 3</div>
</div>
<div className="B">
<div className="base-div1">
<h2 onMouseOver={this.mouseOver} onMouseOut={this.mouseOut}>Project 1</h2>
</div>
</div>
mouseOver(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "block";
}
mouseOut(e){
const hiddenDiv1 = document.querySelector(".hidden-div1");
hiddenDiv1.style.display = "none";
}
Problem is, considering I have 3 different hidden-divs and 3 different base-divs, I wanted to make 2 universal mouseOver and mouseOut functions for all of them. The way I tried it, is this:
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.className.slice(-1);
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
hiddenDivSelector.style.display = "block";
}
but it returns "Cannot set property 'display' of undefined".
I tried console logging hiddenDivSelector and it shows an HTML collection and I don't know how to get my element. I've tried reading about it and visiting other questions but I couldn't apply anything to my situation
Event target returns a reference to DOM element. On DOM elements we can use getAttribute method and replace all non-digit characters by ''; result may be used to search DOM and iterate over returned array;
mouseOver(e){
let hiddenDivName = "hidden-div" + e.target.getAttribute('class').replace(/\D/g, '');
let hiddenDivSelector = document.getElementsByClassName(hiddenDivName);
Array.from( hiddenDivSelector ).forEach(el => el.style.display ) = "block";
}

How to re-Order html child elements in Dom based on id value

I have a parent div with some child elements. I want to re-order child elements based on two id values. for example 1,4. It means to grab the item with id 1 and insert it above the item with id 4.
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Making a drag and drop component for react. And this is what i have tried
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
But problem is when i grab the first element and want to put it on the bottom (last child). It fails to do so. How to put a child element after last child with insertBefore() method?
Don't know how you are using insertBefore() but there should not be any issues:
Update: The issue could be that your code is running before the DOM is fully loaded. You can wrap your code with DOMContentLoaded:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const element = document.getElementById('1') //dragStart
const targetElement = document.getElementById('4') //dragEnter
const parent = document.querySelector('.parent') // drop
parent.insertBefore(element, targetElement)
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Placing the first element as the last element using nextSibling:
<script>
document.addEventListener('DOMContentLoaded', (event) => {
const parentNode = document.querySelector('.parent');
const element = document.getElementById('1') //dragStart
const targetElement = document.querySelector('.parent').lastElementChild //get last child
parentNode.insertBefore(element, targetElement.nextSibling);
});
</script>
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Note: This answers the original question. The question has now been edited to reference React. You wouldn't use the following in a React project. You'd reorder the state that the DOM represents, and then let React handle updating the DOM.
You're right to use insertBefore:
function moveElement(move, before) {
// Get the element to move
const elToMove = document.getElementById(move);
// Get the element to put it in front of
const elBefore = document.getElementById(before);
// Move it
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
function moveElement(move, before) {
const elToMove = document.getElementById(move);
const elBefore = document.getElementById(before);
elBefore.parentNode.insertBefore(elToMove, elBefore);
}
setTimeout(() => {
moveElement("1", "4");
}, 800);
<div class="parent">
<div id="1">First</div>
<div id="2">Second</div>
<div id="3">Third</div>
<div id="4">Fourth</div>
<div id="5">Fifth</div>
</div>
Side note: I suggest avoiding having id values that start with digits. Although they're perfectly valid HTML and they work just fine with getElementById, they're a pain if you need to target them with CSS, because a CSS ID selector (#example) can't start with an unescaped digit. For instance, document.querySelector("#1") fails. You have to escape the 1 with a hex sequence, which isn't terrifically clear: document.querySelector("#\\31") (the characters \, 3, and 1: 0x31 = 49 = the Unicode code point for 1).

Sorting Child Elements Based on 'h2' Tag

I have an an example page containing several categories. Each category is wrapped in a .items class which contains an h2 title tag and several links. My goal is to sort each of those categories alphabetically based on the h2 tag.
I found several examples on how to do this, but they were in jquery. I want to do this only in javascript. I found some code that will sort divs but not by the divs's h2 tag.
HTML
<div id="mainContainer" class="column-container row">
<div class="item column">
<h2>Testimonials</h2>
Testimonial slider
</div>
<div class="item column">
<h2>Directories</h2>
Staff Directory
</div>
<div class="item column">
<h2>FAQ</h2>
</div>
<div class="item column">
<h2>Forms</h2>
Simple contact form - WIP
Online payment form using Network Merchants - WIP
Form with attachment
</div>
</div>
JavaScript
sortCategory('#mainContainer');
function sortCategory(s) {
Array.prototype.slice.call(document.body.querySelectorAll(s)).sort(function sort (ea, eb) {
var a = ea.textContent.trim();
var b = eb.textContent.trim();
if (a < b) return -1;
if (a > b) return 1;
return 0;
}).forEach(function(div) {
div.parentElement.appendChild(div);
});
}
How can I modify the javascipt code to sort each .item by the h2 tag?
Solution
With the help of others I figured it out and wanted to share the code. I also formatted the code to be easily read.
//****************************************
// Sort Categories Alphabetically
//****************************************
function sortCategory(elementContainer)
{
var allElements = document.body.querySelectorAll(elementContainer);
Array.prototype.slice.call(allElements).sort(byAlphabet).forEach(function(div)
{
div.parentElement.appendChild(div);
});
}
function byAlphabet(first, second)
{
var order = 0;
var first = first.querySelector('h2').textContent.trim();
var second = second.querySelector('h2').textContent.trim();
first > second ? order = 1 : order = -1;
return order;
}
//Call sortCategory function and pass in the container you want sorted
sortCategory('#mainContainer>.item');
Change ea.textContent.trim() to ea.querySelector('h2').textContent.trim()
and
change eb.textContent.trim() to eb.querySelector('h2').textContent.trim()
This will basically say check each div's first H2 element, rather than the div.
Hope I was helpful!

Categories