Changing class without variables in javascript - javascript

I have attached 2 of my divs below. When the icon inside heart is clicked, if the class name is far then it should change from far to fas. But if the class name has been changed to fas, it should change back to far. I'm not sure how to do this becuase I have many divs.
<div class="cont">
<h2>A header</h2>
<div class="heart">
<i onclick="like(example)" class="fas fa-heart"></i>
</div>
</div>
<div class="cont">
<h2>A header#2</h2>
<div class="heart">
<i onclick="like1()" class="fas fa-heart"></i>
</div>
</div>
This is the javascript I currently have.
function like(example){
if(example.classList=="far fa-heart"){
example.classList.toggle="fas fa-heart";
} else{
example.classList.toggle="far fa-heart";
}
}
I want this to be in just 1 function without making a variable for all the tags in javascript. I'm still learning... Thanks for your help!

I can't find a good dupetarget for this. Basically, you can hook click on a parent element containing all of these (body if nothing else) and only take action if the click passed through the fa-heart element when bubbling:
theContainer.addEventListener("click", function(event) {
// Did the click pass through an `.fa-heart` element?
const heart = event.target.closest(".fa-heart");
if (heart && this.contains(heart)) {
// Yes, and that element is inside this container; toggle it
heart.classList.toggle("far");
heart.classList.toggle("fas");
}
});
See closest, contains, and toggle for details.
Live Example:
const theContainer = document.body;
theContainer.addEventListener("click", function(event) {
// Did the click pass through an `.fa-heart` element?
const heart = event.target.closest(".fa-heart");
if (heart && this.contains(heart)) {
// Yes, and that element is inside this container; toggle it
heart.classList.toggle("far");
heart.classList.toggle("fas");
}
});
.fa-heart {
display: inline-block;
width: 20px;
color: white;
}
.fas {
background-color: red;
}
.fas::after {
content: 'fas';
}
.far {
background-color: green;
}
.far::after {
content: 'far';
}
<div class="cont">
<h2>A header</h2>
<div class="heart">
<i class="fas fa-heart"></i>
</div>
</div>
<div class="cont">
<h2>A header#2</h2>
<div class="heart">
<i class="fas fa-heart"></i>
</div>
</div>

Edited the answer to add the HTML and import fontawesome, since my first answer was done on my phone.
function toggleHearts() {
document.querySelectorAll(".heart")
.forEach(elm => elm.addEventListener("click", (e) => {
e.target.classList.toggle("far");
e.target.classList.toggle("fas");
}));
}
toggleHearts()
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta2/css/all.min.css" integrity="sha512-YWzhKL2whUzgiheMoBFwW8CKV4qpHQAEuvilg9FAn5VJUDwKZZxkJNuGM4XkWuk94WCrrwslk8yWNGmY1EduTA==" crossorigin="anonymous" referrerpolicy="no-referrer"
/>
<div class="container">
<h2>A header</h2>
<div class="heart">
<i class="fas fa-heart"></i>
</div>
</div>
<div class="container">
<h2>A header#2</h2>
<div class="heart">
<i class="fas fa-heart"></i>
</div>
</div>

You can use a function that accepts an HTML element as a parameter and toggles the classes.
function like1(element) {
element.classList.toggle("fas");
element.classList.toggle("far");
}
.fas {
color: green;
}
.far {
color: red;
}
<div class="cont">
<h2>A header</h2>
<div class="heart">
<i onclick="like1(this)" class="fas fa-heart">Like</i>
</div>
</div>
<div class="cont">
<h2>A header#2</h2>
<div class="heart">
<i onclick="like1(this)" class="fas fa-heart">Like</i>
</div>
</div>

Related

How do I save multiple instances of the same JS code on firebase?

Sorry I don't code much and have adapted this code, so help would be greatly appreciated.
I'm trying to emulate a shopping page where you can 'like' a product and shows number of 'likes' for each product.
What is happening:
When I click on different instances of the 'like' button they get saved as one instance on firebase and all the 'like' counters show the same number of 'likes'
What I want:
Every time I click a different instance of the 'like' button I want it saved as a different instance on firebase so the counts are different for each 'like' button.
var dCounters = document.querySelectorAll('.CountLike');
[].forEach.call(dCounters, function(dCounter) {
var el = dCounter.querySelector('button');
var cId = dCounter.id;
var dDatabase = firebase.database().ref('Like Number Counter').child(cId);
// get firebase data
dDatabase.on('value', function(snap) {
var data = snap.val() || 0;
dCounter.querySelector('span').innerHTML = data;
});
// set firebase data
el.addEventListener('click', function() {
dDatabase.transaction(function(dCount) {
return (dCount || 0) + 1;
});
});
});
.CountLike div {
display: inline-flex;
}
.item-like {
font-size: 18px;
display: inline-block;
margin-top: 10px;
}
.counterStat {
margin-right: 15px;
margin-top: 5px;
}
.heart {
width: 32px;
height: 32px;
}
.btn {
background: none;
border: none;
cursor: pointer;
}
<div>
<div class="store-action">
<div class="CountLike" id="Like Count">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
<div>
<div class="store-action">
<div class="CountLike" id="Like Count">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
Below snippet should do it for now. Both of your elements have the same id value set which is set as id="Like Count"
So right now you just end up writing and reading from the same field for every cell you have.
As it is also stated on this link you should always make sure the id values you assign are unique.
<div>
<div class="store-action">
<div class="CountLike" id="xyz">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>
<div>
<div class="store-action">
<div class="CountLike" id="xyzt">
<div class="likes">
<span class="counterStat">0</span>
<button class="btn"><img src="https://www.svgrepo.com/show/164008/heart.svg" class="heart" alt="the heart-like button"></button>
</div>
</div>
</div>
</div>

How toggle elements inside this intersection observer?

I have a few elements I want to toggle wth a function, but by some reason I can't make it work. When the toggle function is executed, the console logs "cannot read the property classList of Undefined". But if I log them before the function starts I can see the elements.
Javascript
const toggle = element => {
element.classList.toggle('toggle');
};
let numberOfProyects = document.getElementsByClassName('portfolio__item'),
proyects = [],
for (var i = 0; i < numberOfProyects.length; i++) {
proyects[i] = document.getElementById(`proyect${i+1}`);
console.log(proyects[i]);
new IntersectionObserver(()=>{
toggle(proyects[i])
},{threshold: .6}).observe(proyects[i]);
};
HTML
<div class="portfolio__item toggle" id="proyect1">
<h3 class="portfolio__item-title">Podomoro Timer</h3>
<img class="portfolio__item-img" src="assets/images/Captura de pantalla (316).png">
<div class="portfolio__item-links">
<div class="overlay portfolio__item-links-overlay">
<a target="_blank" href="https://js-codetalker.github.io/Timer/" class="portfolio__item-links-overlay-link">
<img src="assets/images/world.svg" class="portfolio__item-links-overlay-link-img">
<p class="portfolio__item-links-overlay-link-txt">Go proyect</p>
</a>
</div>
<div class="overlay portfolio__item-links-overlay">
<a target="_blank" href="https://github.com/Js-codetalker/Timer" class="portfolio__item-links-overlay-link">
<img src="assets/images/github.svg" class="portfolio__item-links-overlay-link-img">
<p class="portfolio__item-links-overlay-link-txt">Go github</p>
</a>
</div>
</div>
</div>
<div class="portfolio__item toggle" id="proyect2">
<h3 class="portfolio__item-title">Sample Restaurant</h3>
<img class="portfolio__item-img" src="assets/images/Captura de pantalla (317).png">
<div class="portfolio__item-links">
<div class="overlay portfolio__item-links-overlay">
<a target="_blank" href="https://js-codetalker.github.io/restaurant-example/" class="portfolio__item-links-overlay-link">
<img src="assets/images/world.svg" class="portfolio__item-links-overlay-link-img">
<p class="portfolio__item-links-overlay-link-txt">Go proyect</p>
</a>
</div>
<div class="overlay portfolio__item-links-overlay">
<a target="_blank" href="https://github.com/Js-codetalker/restaurant-example" class="portfolio__item-links-overlay-link">
<img src="assets/images/github.svg" class="portfolio__item-links-overlay-link-img">
<p class="portfolio__item-links-overlay-link-txt">Go github</p>
</a>
</div>
</div>
</div>
What I want is to create a different observer for each element in order to remove the class "toggle" when it reach the expected space in the viewport
(almost) Always use let instead of var when defining variables inside for loops
Also, you don't need store your elements in second array.
const toggle = element => {
element.classList.toggle('toggle');
console.log(element);
};
const proyects = document.getElementsByClassName('portfolio__item');
for (let i = 0; i < proyects.length; i++) {
new IntersectionObserver(() => {
toggle(proyects[i])
}, {
threshold: .6
}).observe(proyects[i]);
};
.container
{
border: 1px solid black;
resize: both;
overflow: auto;
width: 20em;
height: 10em;
}
.container > :after
{
content: attr(id) " class is " attr(class);
}
.container > :not(.toggle) {
background-color: pink;
}
.container >.toggle {
background-color: lightgreen;
}
<div class="container">
<div class="portfolio__item toggle" id="proyect1">
<h3 class="portfolio__item-title">Podomoro Timer</h3>
</div>
<div class="portfolio__item" id="proyect2">
<h3 class="portfolio__item-title">Sample Restaurant (has not toggle by default)</h3>
</div>
<div class="portfolio__item toggle" id="proyect3">
<h3 class="portfolio__item-title">test</h3>
</div>
<div class="portfolio__item toggle" id="proyect4">
<h3 class="portfolio__item-title">test</h3>
</div>
</div>

How to select dynamic javascript dom element

I created a javascript script to create a couple of divs. Now i want to use javascript again but i can't get a way of doing it.
data.map((_r) => {
classesList.innerHTML += `
<div class="class" name=${_r.name} >
<div class="top">
<h1>${_r.name}</h1>
<i class="fas fa-arrow-down"></i>
</div>
<div class="bottom">
<p>${_r.count}</p>
</div>
</div>
`;
});
Using the document.querySelectorAll(".class") to get the inserted elements returns an empty NodeList
data = [{ name:'Steve',count:10 },{name:'Everst',count:1}];
let html='';
data.map((_r) => {
html += `
<div class="class" name=${_r.name} >
<div class="top">
<h1>${_r.name}</h1>
<i class="fas fa-arrow-down"></i>
</div>
<div class="bottom">
<p>${_r.count}</p>
</div>
</div>
`;
});
document.body.innerHTML=html;
let divs = document.querySelectorAll(".class");
console.log(divs)

index.js:28 Uncaught TypeError: Cannot read property 'addEventListener' of null ( at toggleActiveOnClick and at Array.forEach)

For a project I have to evolve a piece of code but I can not. This bit of code initially allows me to toggle the 4 clickable elements of a certain form and it works perfectly.
const choices = document.querySelectorAll('.clickable');
const toggleActiveClass = (event) => {
event.currentTarget.classList.toggle('active');
};
const toggleActiveOnClick = (choice) => {
choice.addEventListener('click', toggleActiveClass);
};
choices.forEach(toggleActiveOnClick);
However, now I have to make sure that when I select one or the other of the first 2 elements I can not toggle the other and the same for the next 2. I tried this piece of code but when I open the console in chrome tool i get the error message present in the title of this post. Here is the piece of code in question:
const upsell = document.querySelector('.clickable > .fas fa-shopping-cart');
const crossell = document.querySelector('.clickable > .fas fa-cart-plus');
const standard = document.querySelector('.clickable > .fas fa-gift');
const discount = document.querySelector('.clickable > .fas fa-percent');
const choices = [ upsell, crossell, standard, discount ];
const toggleActiveClass = (event) => {
event.currentTarget.classList.toggle('active');
};
const toggleActiveOnClick = (choice) => {
if (choice === upsell.addEventListener('click', toggleActiveClass)) {
crossell.classList.remove('active');
} else if (choice === crossell.addEventListener('click', toggleActiveClass)) {
upsell.classList.remove('active');
} else if (choice === standard.addEventListener('click', toggleActiveClass)) {
discount.classList.remove('active');
} else if (choice === discount.addEventListener('click', toggleActiveClass)) {
standard.classList.remove('active');
}
};
choices.forEach(toggleActiveOnClick);
Here is the corresponding html
<div class="form-group">
<label for="bundle-type">Bundle Type</label>
<div class="d-flex flex-wrap justify-content-center pt-4">
<div id="test1">
<div class="clickable">
<i class="fas fa-shopping-cart"></i>
<small>Upsell</small>
</div>
</div>
<div id="test2">
<div class="clickable">
<i class="fas fa-cart-plus"></i>
<small>Cros-sell</small>
</div>
</div>
</div>
<label for="bundle-type">Offer Type</label>
<div class="d-flex flex-wrap justify-content-center pt-4">
<div id="test3">
<div class="clickable">
<i class="fas fa-gift"></i>
<small>Standard</small>
</div>
</div>
<div id="test4">
<div class="clickable">
<i class="fas fa-percent"></i>
<small>Discounted</small>
</div>
</div>
</div>
</div>
And the CSS
.clickable i {
font-size: 24px;
margin-bottom: 10px;
}
.clickable:hover i {
color: #167FFB;
}
.clickable.active {
color: inherit;
border-color: inherit;
}
.clickable.active i {
color: #0F60C4;
}
Your selectors are just wrong, and you could have verified them (upsell and the others) with debugging/logging.
class="fas fa-shopping-cart" asssigns two classes to the element, fas and fa-shopping-cart. While it may be possible that after digesting the specification someone can come up with a selector for multiple classes, you could simply ignore fas (which is the same for all of them), and go for the specific ones instead:
console.log("Original attempt:");
const upsell = document.querySelector('.clickable > .fas fa-shopping-cart');
const crossell = document.querySelector('.clickable > .fas fa-cart-plus');
console.log(upsell);
console.log(crossell);
console.log("Simplified attempt:");
const upsellSimple = document.querySelector('.clickable > .fa-shopping-cart');
const crossellSimple = document.querySelector('.clickable > .fa-cart-plus');
console.log(upsellSimple);
console.log(crossellSimple);
<label for="bundle-type">Bundle Type</label>
<div class="d-flex flex-wrap justify-content-center pt-4">
<div id="test1">
<div class="clickable">
<i class="fas fa-shopping-cart"></i>
<small>Upsell</small>
</div>
</div>
<div id="test2">
<div class="clickable">
<i class="fas fa-cart-plus"></i>
<small>Cros-sell</small>
</div>
</div>
</div>
What others tried to point out: addEventListener() has no meaningful return value:
Return value: undefined
The comparisons (if (choice === xy.addEventListener(...)){...}) can not really do anything useful.

Moving DIV disable button 0px left-margin

I have a little problem. Im trying to create my own slider using jQuery and some css/javascript.
I got my slider to work moving a Div 660px to the left and right by clicking a button.
But, I would like to have the right button disabled when the left margin is 0. And I would like the whole div to rewind back to 0px after some clicks.
Is this possible?
Here is my code:
<script language="javascript">
function example_animate(px) {
$('#slide').animate({
'marginLeft' : px
});
}
</script>
<div class="container">
<div class="row">
<div class="contr-left">
<a type="button" value="Move Left" onclick="example_animate('-=600px')"><i class="fa fa-chevron-circle-left fa-2x" aria-hidden="true" ></i></a>
</div>
<div id="carreview" class="carousel" data-ride="carousel" style="opacity: 1; display: block;">
<div class="wrapper-outer">
<div class="wrapper-inner" id="slide">
<?php get_template_part('loop-reviews');?>
</div>
</div>
<div class="contr-right">
<a type="button" value="Move Right" onclick="example_animate('+=600px')"><i class="fa fa-chevron-circle-right fa-2x" aria-hidden="true" ></i></a>
</div>
</div>
</div>
</div>
The code im using is from this page: Page
Well, with the code from Rayn i got the button to hide, but now it won't show when left-margin is 1px or something. So I'm trying this now: (doesn't work btw)
Im trying this now: (doesn't work btw)`function example_animate(px) {
$('#slide').animate({
'marginLeft' : px
});
var slidemarginleft = $('#slide').css('margin-left'); //this gets the value of margin-left
if(slidemarginleft == '0px'){
$('.contr-right').hide();
} else (slidemarginleft == '1px') {
$('.contr-right').show();
}
}`
Also I'm seeing that the margin-left isn't set in the style sheet but in the
<div style="margin-left:0px">content</div>
You can do an if statement after each click to check if the element has 0px
function example_animate(px) {
$('#slide').animate({
'marginLeft' : px
});
var slidemarginleft = $('#slide').css('margin-left'); //this gets the value of margin-left
if(slidemarginleft == '0px'){
$('.contr-right').hide();
}
}
Use this
function example_animate(px) {
$('#slide').animate({
'marginLeft' : px
});
if($('.xman').css("marginLeft")=='0px'){
$('.contr-left').attr('disabled',true);
}
}
Disable Button when margin-left: 0px
You can use jquery's .prop() method to disable a button if a condition is met. Here is an example (in pseudo code):
function disableButton() {
$(element).prop('disabled', true);
}
function checkMargin() {
if ($(element).css("margin") === 0) {
disableButton()
}
}
checkMargin()
Rewind to 0px after some clicks
Here, I'd just set a count for clicks and trigger a function when it meets the threshold you want. Pseudo code:
var threshold = 5
var clicks = 0
$(element).click(function(){
clicks++
if (clicks === 5) {
rewind();
}
clicks - 5
})
function rewind() {
$(element).css( "margin", "0" );
checkMargin()
}
Here is my working code, thanks for all the help:
<script>
function example_animate(px) {
$('#slide').animate({
'marginLeft' : px
});
var slidemarginleft = $('#slide').css('margin-left'); //this gets the value of margin-left
if(slidemarginleft < '-3000px'){
$('#slide').animate({marginLeft: '0px'}, 400);
}
var hide = $('#slide').css('margin-left'); //this gets the value of margin-left
if(hide >= '-50px'){
$('.contr-left').addClass('showMe');
}
var hideMe = $('#slide').css('margin-left'); //this gets the value of margin-left
if(hideMe <= '-50px'){
$('.contr-left').removeClass('showMe');
}
}
</script>
<!-- /Review script -->
<section id="reviews">
<div class="container">
<div class="row">
<div class="container">
<div class="row">
<h4>Reviews</h4>
</div>
</div>
</div>
</div>
<div class="container">
<div class="row">
<div class="col-sm-1" style="width:40px;">
<div class="row">
<div class="contr-left">
<a type="button" value="Move Left" onclick="example_animate('+=510px')"><i class="fa fa-chevron-circle-left fa-2x" aria-hidden="true" ></i></a>
</div>
</div>
</div>
<div class="col-sm-10">
<div class="row">
<div id="carreview" class="carousel" data-ride="carousel" style="opacity: 1; display: block;">
<div class="wrapper-outer">
<div class="wrapper-inner" id="slide" style="margin-left:0px;">
<?php get_template_part('loop-reviews');?>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-1" style="width:40px;">
<div class="row">
<div class="contr-right">
<a type="button" value="Move Right" onclick="example_animate('-=510px')"><i class="fa fa-chevron-circle-right fa-2x" aria-hidden="true" ></i></a>
</div>
</div>
</div>
</div>
<div class="btn btn-06">Bekijk alle reviews</div>
</div>
</section>

Categories