Classlist validation for unique selected item - javascript

So I'm trying to create a chat application like messenger.
When I press the button, a new conversation should be started. I want to add a list item in my overview bar on the left but there can only be one selected, and that one has the 'history-item-selected' classname. So every new convo gets that classname, while the others ones get another classname to change it's appearance but it won't work.
const newConvoButton = document.getElementById("newmessage");
const addNewConvo = (e) => {
e.preventDefault();
const myMessages = document.getElementById('history');
let newListItem = document.createElement('li');
newListItem.textContent = "user " + Math.floor(Math.random(2 - 100) * 100);
myMessages.appendChild(newListItem);
if (newListItem.classList = 'history-item-selected') {
newListItem.classList.add('history-item-selected');
} else {
newListItem.classList.add('history-item')
};
};
newConvoButton.addEventListener('click', addNewConvo);
<main>
<div id="top">
<span>
<h2>My conversations</h2>
</span>
<button type="submit" id="newmessage">+</button>
</div>
<div id="messagecontainer">
<ul id="history"></ul>
<id id="chatscreen">
<ul id="messages">
<li>yolo</li>
</ul>
<div id="messagebottom">
<input type="text" placeholder="Start met typen" size="28" height="auto"> <button type="submit">Send</button>
</div>
</id>
</div>
</main>

There is a "tautological" way to do what you are trying to do.
var addNewConvo = (e)=> {
var nodes = document.querySelectorAll(".history-item-selected");
nodes.forEach(function(elem) {
this.classList.remove("history-item-selected");
});
e.target.classList.add("history-item-selected");
}
In your CSS, you should have something like
.history-item {
/*Styles for history-item*/
}
.history-item.history-item-selected {
/*Styles for elements with both */
}

Related

Why event listener does'n work others time same as first?

I have problem with this javascript code that I cant solve at all
btnClaim.addEventListener("click", () => {
rewardCurrent.style.display = "none";
claimedRewards.push(currentReward);
rewardsList.innerHTML = ``;
claimedRewards.forEach(function (rew, i) {
const html = `
<div class="reward">
<div class="img-text-cont">
<img src="${rew.imgUrl}" alt="">
<div class="text-cont">
<p class="claimed-reward-title">${rew.title}</p>
<p class="claimed-reward-price">$${rew.price}</p>
</div>
</div>
<div class="claimed-rewards-action">
<button class="btn-sell2" id="${i}">Sell</button>
<button class="btn-ship">Ship</button>
</div>
</div>
`;
rewardsList.insertAdjacentHTML("afterbegin", html);
});
const btnsShip = document.querySelectorAll(".btn-ship");
const btnsSell = document.querySelectorAll(".btn-sell2");
btnsSell.forEach(function (sell) {
sell.addEventListener("click", () => {
balance += Number(claimedRewards[sell.id].price);
balanceSum.textContent = `$${balance}`;
claimedRewards.splice(Number(sell.id), 1);
rewardsList.innerHTML = ``;
claimedRewards.forEach(function (rew, i) {
const html = `
<div class="reward">
<div class="img-text-cont">
<img src="${rew.imgUrl}" alt="">
<div class="text-cont">
<p class="claimed-reward-title">${rew.title}</p>
<p class="claimed-reward-price">$${rew.price}</p>
</div>
</div>
<div class="claimed-rewards-action">
<button class="btn-sell2" id="${i}">Sell</button>
<button class="btn-ship">Ship</button>
</div>
</div>
`;
rewardsList.insertAdjacentHTML("afterbegin", html);
});
});
});
btnsShip.forEach(function (ship) {
ship.addEventListener("click", () => {
claimedRewardsClose.style.display = "none";
wheelCont.style.filter = "blur(10px)";
thanksCont.style.display = "flex";
});
});
});
I have claim.addEventListener() that push some products to claimedRewards array, than insertAdjacentHTML() method fill shopping chart with that products information and two buttons(sell and ship), I have problem with sell button, when I click sell button on one product found will transfer back to wallet and this product will disappear from the chart and It is all good, but when I want to sell second product sell button doesn't work, it work if I hit claim button again and add new product, but again I only can sell one product, but second not, again it is possible if I hit again claim button and add new product. Is there any solution for my problem?

Remove class if id's the correct ID

Looking to remove a class if a certain button is clicked.
<div class="slide-container">
<section class="about" id="slide-0">
<div class="menu-total">
<nav class="nav">
<button class="nav_link home" onclick="slideTo('slide-2')">HOME</button>
<button class="nav_link about" onclick="slideTo('slide-0')">ABOUT</button>
<button class="nav_link fun-stuff" onclick="slideTo('slide-1')">FUN STUFF</button>
<button class="nav_link professional" onclick="slideTo('slide-3')">PROFESSIONAL</button>
<button class="nav_link contact" onclick="slideTo('slide-4')">CONTACT</button>
</nav>
<div class="hamburger">
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
<span class="hamburger__patty"></span>
</div>
</div>
The one I want to remove the class on is the HOME button. So "slideTo('slide-2)". If it's clicked on the others then the class is kept. I believe someone is either wrong with my loop or not getting the ID correctly of the items/
function slideTo(slideId) {
const slide = document.getElementById(slideId);
slide.scrollIntoView({
behavior: 'smooth'
})
// above this line works fine
let nonHome = document.querySelectorAll('.slide-container section');
let nonHomeID = document.getElementById('slide-2');
var i;
setTimeout(function(){
for (i=0; i < nonHome.length; i++ ){
// i believe it's somewhere here it is wrong
if (nonHome[i].id != nonHomeID){
nonHome[i].classList.add("nav-visibility");
} else{
nonHomeID.classList.remove("nav-visibility");
}
}
}, 1000)
}
If you can use jquery library, you can write in the HTML:
<button class="nav_link" data-value="home">HOME</button>
...
and then in the JS code:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // Get the data-value clicked
$(".nav_link").each(function() { // Loop through all elements of the class 'nav-link'
var v = $(this).data("value");
if (v == valueClicked) {
$(this).removeClass("nav-visibility");
} else {
$(this).addClass("nav-visibility");
}
)
}
Not much simpler, but the HTML is cleaner.
Simpler version if it is not required to browse through all buttons at each button click:
$(".nav_link").on("click", function() {
var valueClicked = $(this).data("value"); // The value of the button clicked by the user
if (valueClicked == "home") {
$(this).removeClass("nav-visibility");
console.log('remove')
} else { $(this).addClass("nav-visibility");
console.log('add')
}
});

Changing inner values in a list of items

Let say I have a HTML code:
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
and so on...
and JS:
function addAmount(el) {
let amount = document.querySelector(".amount");
let addAmount = parseInt(amount.innerHTML);
if (el.classList.contains("add")) {
addAmount = addAmount + 1;
amount.innerHTML = addAmount;
}
return addAmount;
}
document.addEventListener("click", e => {
addAmount(e.target);
});
This code works only for first li element. I would like to know how I can obtain a code in which every button from each li element is responsible for only one element (one button adds value for one li element).
You shouldn't be searching the document at all. You can use document.querySelectorAll(".amount") to get a list of all the amount fields, but you need to figure out which index to use.
Instead, you can use previousElementSibling or nextElementSibling to get the element before or after the button you clicked on.
function addAmount(el) {
let amountElement, adjustAmount
if (el.classList.contains("add")) {
adjustAmount = 1;
amountElement = el.nextElementSibling;
} else {
adjustAmount = -1;
amountElement = el.previousElementSibling;
}
amountElement.innerText = parseInt(amountElement.innerText) + adjustAmount;
}
document.addEventListener("click", e => {
addAmount(e.target);
});
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>
<li>
<button class="add">+</button>
<span class="amount">0</span>
<button class="substract">-</button>
</li>

Issues with code for a simple on page search

I am trying to build a simple on page search that uses event listeners to look at a containers data and then hides that whole container if it doesn't have the required information.
So far I have:
// get search element
let searchInput = document.getElementById ('searchInput');
// add event listener
searchInput.addEventListener ('keyup', searchPage);
function searchPage(){
//search input detection
let searchValue = document.getElementById('searchInput').value;
//set parameters to search from
let parent = document.getElementById('product-container');
let child = parent.getElementsByTagName('span');
for(let i = 0;i < child.length;i++){
let a = child[i];
if(a.innerHTML.indexOf(searchValue) >= -1) {
child[i].parentNode.style.display = '';
} else {
child[i].parentNode.style.display = 'none';
};
};
};
But this only acts on the first product-container it finds, there are 5 such containers on the page.
How do I make this look through all containers, but hide the ones that don't contain any of the information typed in the search bar.
I am getting products from an API so using html replace to add to the following template:
<script id="template" type="text/template">
<div class="product">
<div class="product--header">{{ type }}</div>
<div class="product--image"><img src="../app/assets/images/no-image.png" alt="no image"> </div>
<div class="product--information" id="product--information">
<div class="product--title"><span>{{ name }}</span></div>
<!--This is just a place holder we would house the prices here if they were on the API -->
<div class="product--price">£55</div>
<div class="product--brand"><strong>Brand:</strong><span> {{ brand }}</span></div>
<div class="product--colour"><strong>Colour:</strong><span> {{ colour }}</span></div>
<div class="product--sizes">
<select>
<option value="" disabled selected>Select Size </option>
{{ options }}
</select>
</div>
<div class="product--description"><strong>Description:</strong><br><div class="product--description__content"><span> {{ description }} </span></div></div>
<div class="product--code"><strong>Product ID:</strong><span> {{ productid }}</span></div>
<div class="product--buttons">
<button class="btn--buy" aria-label="Add to Basket">Add to basket</button>
<button class="btn--save" aria-label="Save for Later">Save for later</button>
</div>
<button class="product--add-to-wishlist" aria-label="Add to Wishlist"><i class="fas fa-heart"></i></button>
</div>
</div>
</script>
The search box code is as follows:
<input type="text" name="search" id="searchInput" placeholder="Enter Search...">
and the code that the template goes into is:
<div id="product-container">
<div class="featured"></div>
<div class="products"></div>
</div>
Because you have multiple product containers, use document.getElementsByClassName() instead of document.getElementById() and provide product-container class as argument.
let searchInput = document.getElementsByClassName ('container');
You need to modify searchPage() method. Instead of using document to find searchValue and parent use this.
let searchValue = this.getElementsByClassName('searchInput')[0].value;
let parent = this.getElementsByClassName('container')[0];
Please, add HTML code.
EDIT: If I understand correctly you have one search input which will search multiple product containers. Here is one simple example, which you can easily apply to your problem.
HTML:
<input type="text" name="search" id="searchInput" placeholder="Enter Search...">
<div class="product-container">
<span class="product">Kiwi</span>
<p>Kiwi description</p>
</div>
<div class="product-container">
<span class="product">Banana</span>
<p>Banana description</p>
</div>
<div class="product-container">
<span class="product">Apple</span>
<p>Apple description</p>
</div>
JS:
let searchInput = document.getElementById ('searchInput');
searchInput.addEventListener ('keyup', searchPage);
function searchPage(){
let searchValue = this.value.toUpperCase();
let products = document.getElementsByClassName('product');
for(let i = 0; i < products.length; i++) {
console.log(products[i].innerHTML.toUpperCase());
if (products[i].innerHTML.toUpperCase().indexOf(searchValue) > -1)
products[i].parentNode.style.display = '';
else
products[i].parentNode.style.display = 'none';
};
};
CSS:
.product-container {
display: flex;
flex-direction: column;
margin-bottom: 10px;
background: grey;
}
.product-container span {
font-size: 20px;
}
.product {
display: block;
}
https://jsfiddle.net/gardelin/koc5eg6v/25/

able to Select and deselect multiple elements in html and keep track of what has been selected

My HTML template will be appended many times depending upon the backend. So, I want to select the topics and send the id of selected elements. How to do it?
Right now I can only select and also after that i can't de-select it too.
Help!!
My Jquery code to select:
$(document.body).click(function(evt){
var clicked = evt.target;
var currentID = clicked.id || "No ID!";
document.getElementById(currentID).style.backgroundColor = "#00afbc";
//$(clicked).html(currentID);
})
My Html code:
<div class="container-fluid" id="container-<%=no%>">
<div id="circle" style="background:<%= colorCode %>;" class="col-xs-3">
<div class="text" style="color:<%= textColor %>;">
<%=p ercent %>
</div>
<div class="percent" style="color:<%= textColor %>;">%</div>
</div>
<div id="sideText">
<div class="checkbox col-xs-9 everything-checkbox">
<!--input type="checkbox" class="toggle" /-->
<div id="currentID">
<%=currentID %>
</div>
<div id="question">
<%=t otalQues %> Questions Attempts
</div>
</div>
</div>
</div>
<hr style="width: 100%; color: #d9d9d9; height: 1px; background-color:#d9d9d9; margin-top:0px;margin-bottom:0px;" />
You can just add and remove a class to keep track of the selected items and then just get all the selected items with an ID
$(document).ready(function () {
$(document.body).click(function (evt) {
var clicked = evt.target;
if (!$(clicked).hasClass('selected')) {
$(clicked).addClass('selected');
$(clicked).css('background-color', '#00afbc');
} else {
$(clicked).removeClass('selected');
$(clicked).css('background-color', '');
}
});
});
function getSelected() {
var ids = [];
$('.selected').each(function () {
var id = $(this).attr('id');
if (id) {
ids.push($(this).attr('id'));
}
});
return ids;
}

Categories