This will be a complex sample cause my codes is like a lot...
So basically I just direct to my point. My problem is that I don't know why my list map function is not includes the output function of return when I'm trying to get the document.querySelectorAll() of all the items. Basically something like this.
<div className='gallery' style={ ispost ? { display:'none' } : {display:"block"}}>
{mainList.map((elem,idx) => {
return (
<div className="gallery-container" key={idx}>
<div className="l">
<div className="l-box">
<div className="text">
<div className="hide">
<h2> Onigsahima Tokyo </h2>
</div>
</div>
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
<div className="r">
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
</div>
)
})}
<div className="gallery-container" style={ ispost ? { display:'none' } : {display:"inline-grid"}}>
<div className="l">
<div className="l-box">
<div className="text">
<div className="hide">
<h2> Onigsahima Tokyo </h2>
</div>
</div>
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
<div className="r">
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
<div className="r-box">
<div className="hide">
<div className="color-sample">
<img src={g3} alt="" />
</div>
</div>
</div>
</div>
</div>
</div>
And then I have a useEffect() and I'll show all the codes about it..but don't get confused about it just focus on the querySelectorAll() cause it is the only thing that affect it all the animation
useEffect(() => {
gsap.registerPlugin(ScrollTrigger,CSSRulePlugin)
// gsap.registerPlugin(CSSRulePlugin)
const sections_ = document.querySelectorAll('.gallery-container')
console.log(sections_)
let sections = gsap.utils.toArray(".gallery-container")
console.log(sections)
let scrollTween = gsap.to(sections, {
xPercent: -100 * (sections.length - 1),
ease: "none", // <-- IMPORTANT!
scrollTrigger: {
trigger: ".gallery",
pin: true,
scrub: 3,
end: "+=3000",
// markers:true
}
});
gsap.to(".gallery-container:nth-child(1) .color-sample",{
y:0,
ease:"none",
scrollTrigger:{
trigger:".gallery-container:nth-child(1)",
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:{startColor: "orange", endColor: "green"},
scrub:2,
}
})
const rule = CSSRulePlugin.getRule(`.r-box:after`)
const rule2 = CSSRulePlugin.getRule(`.r-box:before`)
gsap.to([rule,rule2],{
width:'35%',
height:'35%',
background:"yellow",
delay:3,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(1)`,
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:true,
scrub:2,
}
})
gsap.to(`.gallery-container:nth-child(1) .text h2`,{
y:0,
// delay:2,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(1)`,
containerAnimation:scrollTween,
start:"0% 0%",
end:"10% 6%",
// markers:true,
scrub:2,
}
})
sections.forEach((elem,i) => {
gsap.to(`.gallery-container:nth-child(${i+2}) .color-sample`,{
y:0,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
gsap.to(`.gallery-container:nth-child(${i+2}) .text h2`,{
y:0,
// delay:2,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
const rule = CSSRulePlugin.getRule(`.r-box:after`)
const rule2 = CSSRulePlugin.getRule(`.r-box:before`)
gsap.to([rule,rule2],{
width:'35%',
height:'35%',
background:"yellow",
delay:3,
ease:"none",
scrollTrigger:{
trigger:`.gallery-container:nth-child(${i+2})`,
containerAnimation:scrollTween,
start:"center 50%",
end:"center 60%",
// markers:true,
scrub:2,
}
})
})
const arrowsColor = document.querySelectorAll('.arrow.first ion-icon')
const arrows2Color = document.querySelectorAll('.arrow.second ion-icon')
var scrollPos = 0
window.addEventListener('scroll',() => {
if ((document.body.getBoundingClientRect()).top > scrollPos) {
// console.log('Right')
arrowsColor.forEach(elem => {
elem.style.color = 'black'
})
arrows2Color.forEach(elem => {
elem.style.color = 'yellow'
})
} else {
// console.log('Left')
arrowsColor.forEach(elem => {
elem.style.color = 'yellow'
})
arrows2Color.forEach(elem => {
elem.style.color = 'black'
})
}
scrollPos = (document.body.getBoundingClientRect()).top
})
},[])
I don't know if this is easiest way to explain it but this is the best I can..
Note that mainList is a useState so I'll have something like this in my codes
when I am pushing the items in setMainList
useEffect(() => {
axios.get('http://localhost:7777/gallery')
.then(res => {
let del = []
res.data.map((elem,i) => {
console.log(i)
setMainList([...mainList,elem])
})
})
},[])
Yes it works but why the document.querySelectorAll don't detect if there is a new element that I pop in that mainList.map function? I don't understand why..It shouldn't be this kind of way. I always do this but never encounter this bug..
Note that mainList.map() is like visible but it is not include in that querySelectorlAll
Please help badly need it to get this thing done.
Because your useEffect callback runs only once since you haven't added any dependencies. If you want the callback to be invoked whenever mainList changes, add mainList to the useEffect dependency list like so
useEffect(() => {
//...
}, [mainList])
Be careful though! You should use the return callback to remove any listeners or subscriptions you've set up.
useEffect(() => {
//...
function onScroll() {
//...
}
window.addEventListener('scroll', onScroll)
return () => {
// remove listeners
window.removeEventListener('scroll', onScroll)
}
}, [mainList])
I don't know how gsap works, but you might also want to "deregister" the plugin in the tear-down callback, or just use a different useEffect callback with no dependencies to register it only once - you probably still want to "deregister" it either way, but you wont do it every time mainList changes.
Edit: I highly suggest you read the useEffect docs
Edit2: Here's a gist
I want to display comments line by line in a div but the div is closing first and after that the comments are listing.
if (comments.num_replies > 0) {
$("#showComment").append(`<div id="replyContainer${comments.comment_id}" class="replyContainer">`);
comments.comment_replies.forEach((replies) => {
$("#showComment").append(`
<div class="img-comment reply">
<img src="${home_url}img/user.png">
<div class="m-2 p-3 ">
<div class="user-date"><p class="username">#${replies.replier_username}</p><p class="date">${replies.dates}</p></div>
<p class="comment_content">${replies.reply_comment_content.replace(/\n/g, "<br>")}</p>
</div>
</div>
`)
});
$("#showComment").append(`</div>`);
}
You can try something like this
if (comments.num_replies > 0) {
let replies = comments.comment_replies.map((replies) => (
`<div class="img-comment reply">
<img src="${home_url}img/user.png">
<div class="m-2 p-3 ">
<div class="user-date"><p class="username">#${replies.replier_username}</p><p class="date">${replies.dates}</p></div>
<p class="comment_content">${replies.reply_comment_content.replace(/\n/g, "<br>")}</p>
</div>
</div>`
));
$("#showComment").append(`<div id="replyContainer${comments.comment_id}" class="replyContainer">${replies.join('')}</div>`);
}
I'm trying to move a node list (in my case img HTML tags with a class name of ".images") when I reach a specific breakpoint to another parent as the first child of that new parent.
/* BEFORE BREAKPOINT */
<div class="old-parent">
<img class="images">
<div class="new-parent">
</div>
</div>
<div class="old-parent">
<img class="images">
<div class="new-parent">
</div>
</div>
<div class="old-parent">
<img class="images">
<div class="new-parent">
</div>
</div>
/*AFTER REACHING THE BREAKPOINT*/
<div class="old-parent">
<div class="new-parent">
<img class="images">
</div>
</div>
<div class="old-parent">
<div class="new-parent">
<img class="images">
</div>
</div>
<div class="old-parent">
<div class="new-parent">
<img class="images">
</div>
</div>
So far is working when I use a single selector, however when I try to select them all and transfer them to a new parent is not working.
const newParent = document.querySelectorAll('.new-parent');
const images = document.querySelectorAll('.images');
const mediaQ = window.matchMedia('(max-width: 494px)');
const changeImg = (e) => {
if (e.matches) {
newParent.forEach(elem => {
elem.insertBefore(images, elem.childNodes[0]);
})
}
};
mediaQ.addEventListener('change', changeImg);
Then returns an error in the console:
Uncaught TypeError: Failed to execute 'insertBefore' on 'Node': parameter 1 is not of type 'Node'
You need to index images to move one image at a time.
const changeImg = (e, i) => {
if (e.matches) {
newParent.forEach(elem => {
elem.insertBefore(images[i], elem.childNodes[0]);
})
}
};
I have 32 items in my array, all of them have these properties: id, word, image. User has to guess what's in all the images and write their guess in inputs (so 32 inputs in total). I need to check if the input equals my arrays property "word" and then when clicked a button (type submit, all my pic's and inputs are in a form) display some text for example "Oops! Guess again" if wrong and "Yay! You got it correctly" if right. The text should appear below every input. I displayed all the pictures and inputs with a forEach, and i'm using bulma framework for this page:
const wordBox = info.forEach((words) => {
mainColumns.innerHTML += `
<div class="column is-one-quarter">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src=${words.image} alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<input class="input" id="text" type="text" placeholder="Įvesk žodį">
</div>
</div>
<div class="content">
Content
</div>
</div>
</div>
</div>`;
});
Any ideas?
This is how it should look like (the result should appear in content place)
Something like this
I use change instead of a button click
const info = [
{word:"flower",image:"flower.gif"},
{word:"boat",image:"boat.gif"}
];
const mainColumns = document.getElementById("mainColumns");
mainColumns.innerHTML = info.map(({image,word}) =>
`<div class="column is-one-quarter">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src=${image} alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<input class="input" data-word="${word}" type="text" placeholder="Įvesk žodį">
<span class="correct hide">Yay</span>
<span class="wrong hide">NOO</span>
</div>
</div>
<div class="content">
Content
</div>
</div>
</div>
</div>`).join("");
mainColumns.addEventListener("change",function(e) {
const correct = [...mainColumns.querySelectorAll("[data-word]")].map(input => {
if (input.value) {
const correct = input.value === input.dataset.word;
parent = input.closest("div");
parent.querySelector(".correct").classList.toggle("hide",!correct)
parent.querySelector(".wrong").classList.toggle("hide",correct);
return correct ? 1 : 0;
}
else return 0;
}).reduce((a,b)=>a+b);
document.getElementById("correct").innerText = correct;
})
#mainColumns { display:flex; }
.hide { display: none; }
<div id="mainColumns"></div>
Correct: <span id="correct"></span>
What you can do is to filter the word array with word from the input value. Then check if the length is equal zero, No match, if the length is greater than one, then there is a match.
const status = wordBox.filter(item => item.word === inputWord)
I'd move towards keeping the objects and the HTML separate, binding the HTML to the object and vice versa. This means including a couple more properties to your array elements.
let info = [{
image: 'flower.png',
word: 'flower',
content: '',
guess: ''
}];
function bindWords() {
info.forEach((words) => {
mainColumns.innerHTML = `
<div class="column is-one-quarter">
<div class="card">
<div class="card-image">
<figure class="image is-4by3">
<img src=${words.image} alt="Placeholder image">
</figure>
</div>
<div class="card-content">
<div class="media">
<div class="media-content">
<input class="input" data-word="${words.word}" type="text" placeholder="Įvesk žodį" value="${words.guess}">
</div>
</div>
<div class="content">
${words.content}
</div>
</div>
</div>
</div>`;
});
}
bindWords();
check.addEventListener('click', () => {
info = Array.from(document.querySelectorAll('.card')).map(el => ({
image: el.querySelector('img').src,
word: el.querySelector('.input').dataset.word,
guess: el.querySelector('.input').value,
content: el.querySelector('.input').value === el.querySelector('.input').dataset.word ?
'Correct' : 'Incorrect'
}));
bindWords();
});
<div id="mainColumns"></div>
<button id="check">Check Answers</button>
this if my first question here, so sorry for any mistake or if the question is too silly.
*I have a Class with a method that displays a country-card on screen. I need to add an onclick function to save the name of the country so I can access to it from another page. i don't know if there is a way to make it work.
Any ideas?
class UI {
constructor() {
this.cardsContainer = document.querySelector("#cards-container");
}
showCountries(data) {
data.forEach(country => {
let div = `
<div class="card">
// this is the onclick function i need to access
<a onclick="countrySelected(${this.country.borders})">
<div class="card-image">
<img src=${country.flag} alt="">
</div>
<div class="card-info">
<h2 class="card-name"> ${country.name}</h2>
<p><strong>Population:</strong> ${country.population}</p>
<p><strong>Region:</strong> ${country.region}</p>
<p><strong>Capital:</strong> ${country.bogota}</p>
</div>
</a>
</div>
`;
this.cardsContainer.innerHTML += div;
})
}
//method
countrySelected(data) {
sessionStorage.setItem('country', data);
}
}
I assume that the country.name is unique.
class UI {
constructor() {
this.cardsContainer = document.querySelector("#cards-container");
}
showCountries(data) {
data.forEach(country => {
let div = `
<div class="card">
// this is the onclick function i need to access
<a id=${country.name}>
<div class="card-image">
<img src=${country.flag} alt="">
</div>
<div class="card-info">
<h2 class="card-name"> ${country.name}</h2>
<p><strong>Population:</strong> ${country.population}</p>
<p><strong>Region:</strong> ${country.region}</p>
<p><strong>Capital:</strong> ${country.bogota}</p>
</div>
</a>
</div>
`;
this.cardsContainer.innerHTML += div;
document.querySelector(`a[id="${country.name}"]`)
.addEventListener('click', () => countrySelected(country.borders));
})
}
//method
countrySelected(data) {
sessionStorage.setItem('country', data);
}
}
Also, you can refer to this post: Setting HTML Button`onclick` in template literal