Why isn't forEach working after page refresh? - javascript

I want to add and remove 'selected-bullet' class but first time it is working and after refresh browser it is not working. why is not forEach working after page refresh?
const Resume = () => {
const bullets = document.querySelector('.bullets');
const bullet = document.querySelectorAll('.bullet');
[...bullet].forEach((el) => {
el.addEventListener('click', () => {
bullets
.querySelector('.selected-bullet')
.classList.remove('selected-bullet');
el.classList.add('selected-bullet');
});
});
return (
<div className='resume-bullets'>
<div className='bullet-container'>
<div className='bullet-icons'></div>
<div className='bullets'>
<div className='bullet selected-bullet'>
<i className='bi bi-mortarboard-fill bullet-logo'></i>
<span className='bullet-label'>Education</span>
</div>
<div className='bullet'>
<i className='bi bi-clock-history bullet-logo'></i>
<span className='bullet-label'>Work History</span>
</div>
<div className='bullet'>
<i className='bi bi-laptop bullet-logo'></i>
<span className='bullet-label'>Programming Skills</span>
</div>
<div className='bullet'>
<i className='bi bi-bar-chart-line bullet-logo'></i>
<span className='bullet-label'>Projects</span>
</div>
<div className='bullet'>
<i className='bi bi-palette bullet-logo'></i>
<span className='bullet-label'>Interests</span>
</div>
</div>
</div>
</div>
);
};

Related

how to loop through an array to hide items

i have an html form in which the user clicks on the plus sign and it shows you a list of items, if you click on a minus sign it will hide those items
as follows:
<div repeat.for="categoryGrouping of categoryDepartment">
<div class="row">
<div class="col s12 m3 l3">
<div class="card blue-grey darken-1">
<div class="card-content" style="padding:10px">
<span class="card-title white-text truncate">${categoryGrouping.name}</span>
<a if.bind="categoryGrouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="Activate(user, categoryGrouping)"><i class="material-icons">add</i></a>
<a if.bind="!categoryGrouping.hideDetails" class="btn-floating halfway-fab waves-effect waves-light" click.delegate="DeActivate(user, categoryGrouping)"><i class="material-icons">remove</i></a>
</div>
</div>
</div>
</div>
<div repeat.for="categoryGroupingTypes of categoryDepartmentTypes">
<div class="row" if.bind="!categoryGrouping.hideDetails">
<div repeat.for="user of categoryGroupingTypes.users" class="col s12 m3 l3 ">
<div class="card blue-grey darken-1" click.delegate="GetCrewProfiles(user)">
<div class="card-content">
<span class="card-title white-text truncate">${user.firstName} ${user.lastName}</span>
<p>${user.emailAddress || 'mock#email.com'}<br /></p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
in type script
async Activate(user: userListModel[], department: any) {
this.categoryDepartment.forEach(x => {
x.hideDetails = true;
});
this.categoryDepartmentTypes = await this.getTypes(user, department.name);
department.hideDetails = false;
}
async DeActivate(user: userListModel[], department: any) {
department.hideDetails = true;
}
this.categoryDepartment stores my array as follows:
0:{hideDetails:true, name:"Hello"}
1:{hideDetails:false,name:"Test"}
so the above looks as the following image
so now for example if i had to click on the "Hello" plus sign while the details for "Test" is open it would close "Test".
How can i loop through the departments to have it remain open but only when i click the minus then it should close.
I need to pass in the department name to this await this.getTypes(user, department.name); method, so only the item i click on should pass that name in and not all the department names.
does anyone know how i can achieve this?
i tried doing the following
if (this.categoryDepartment.find(x => x.name == department.name)){
this.categoryDepartmentTypes = await this.getTypes(user, department.name);
department.hideDetails = false;
}
but it still does the same thing, it closes on the next item i click on
You hide everything in this loop:
this.categoryDepartment.forEach(x => {
x.hideDetails = true;
});
So you can remove it.

Can i querySelectorAll buttons that previously added with (element.innerHTML)?

I want to make array of all the buttons that inside the article tags.
When i select all buttons -i mentioned- that i added with (element.innerHTML) to use it in my JavaScript code,it doesn't work.
I tried to console.log the selected buttons but console gives me an empty array?
is there a wrong in my way of selection or there's another way to reuse that buttons in my JavaScript code?
const productsCenter = document.querySelector(".products-center");
class Displayproducts {
async getData() {
try {
let response = await fetch("products.json");
let data = await response.json();
let products = data.items;
let mapped = products.map((el) => {
const { title, price } = el.fields;
const image = el.fields.image.fields.file.url;
const id = el.sys.id;
return { title, price, image, id };
});
return mapped;
} catch (err) {
console.log(err);
}
}
}
class UI {
displayProducts(products) {
products.forEach((product) => {
let result = `<article class="product">
<div class="img-container">
<img
src="${product.image}"
alt="product"
class="product-img"
/>
<button class="bag-btn" data-id="${product.id}">
<i class="fas fa-shopping-cart"></i>
add to bag
</button>
</div>
<h3>${product.title}</h3>
<h4>${product.price}</h4>
</article>`;
productsCenter.innerHTML += result;
});
}
}
window.onload = () => {
const product = new Displayproducts();
const ui = new UI();
product.getData().then((products) => {
ui.displayProducts(products);
});
const bagBtns = [...document.querySelectorAll(".bag-btn")];
console.log(bagBtns);
};
<body>
<!-- start of nav bar -->
<nav class="navbar">
<div class="navbar-center">
<span class="nav-icon">
<i class="fas fa-bars"></i>
</span>
<img
src="C:\Users\iman\Desktop\comfy-house\images\logo.svg"
alt="logo"
/>
<div class="cart-btn"></div>
<span class="nav-icon">
<i class="fas fa-cart-plus"></i>
</span>
<div class="cart-items">0</div>
</div>
</nav>
<!-- end of nav bar -->
<!-- start of header -->
<header class="hero">
<div class="banner">
<h1 class="banner-title">furniture collection</h1>
<button class="banner-btn">shop now</button>
</div>
</header>
<!-- end of header -->
<!-- start of cart -->
<div class="cart-overlay">
<div class="cart">
<span class="close-cart">
<i class="fas fa-window-close"></i>
</span>
<h2>your cart</h2>
<div class="content"></div>
<!-- start of cart item -->
<!-- end of cart item -->
</div>
</div>
<!-- end of cart -->
<!-- start of products-->
<section class="products">
<div class="section-title">
<h2>our products</h2>
</div>
<div class="products-center">
</div>
</section>
<!-- end of products -->
<script src="app.js"></script>
</body>
In the window.onload function the const bagBtns = [...document.querySelectorAll(".bag-btn")]; line happened before the ui.displayProducts(products); line - you can see, add console.log('after') inside the callback function and see what happen.
This happen because - read this article
sulotions:
use async await
move the next lines:
const bagBtns = [...document.querySelectorAll(".bag-btn")];
console.log(bagBtns);
after:
ui.displayProducts(products);

How do I apply the same logic to multiple repeated elements?

I am trying to add the same drop down functionality to an amount n of "cards". How could I do this in javascript without having to create new code for each card?
This is my current code that I have tried, but even if I got this working it would only work for the first card: (working example below)
my html:
<div class="ideanode">
<div class="ideanodeheader">Need</div>
<div class="content">
<div class="title">
<h3 contenteditable="True" onclick='this.focus();'>Title</h3>
</div>
<i class="fas fa-sort-down title-arrow"></i>
<div class="maintext">
<textarea placeholder="Text" class="maintextinput"></textarea>
</div>
<i class="fas fa-sort-down maintxt-arrow"></i>
<div class="comments">
<textarea placeholder="Comments" class="commentsinput"></textarea>
</div>
</div>
</div>
js:
var maintxt = document.querySelector(".maintext");
var title = document.querySelector(".title");
var titleArrow = document.querySelector(".title-arrow");
var mainArrow = document.querySelector(".maintxt-arrow");
var comments = document.querySelector(".comments");
var mainArrows = document.querySelectorAll(".maintxt-arrow")
titleArrow.addEventListener('click', function() {
maintxt.classList.toggle("hidden");
mainArrow.classList.toggle("hidden");
if (comments.classList.contains("hidden")){
;
} else {
comments.classList.toggle("hidden");
};
});
mainArrow.addEventListener("click", function() {
comments.classList.toggle("hidden");
});
Example: https://codepen.io/ricodon1000/pen/Baoxwed
I would like for the dropdown arrows to trigger the boxes below them, the upper arrow should trigger only the box below it on, but if both boxes are open both should be closed. The arrow that is below should only trigger the box directly below it on and off (display: none on and off).
Do the same for each node, and limit your querySelector to the group
const ideanodes = [...document.querySelectorAll('.ideanode')];
ideanodes.forEach(ideanode => {
const maintxt = ideanode.querySelector(".maintext");
const title = ideanode.querySelector(".title");
const titleArrow = ideanode.querySelector(".title-arrow");
const mainArrow = ideanode.querySelector(".maintxt-arrow");
const comments = ideanode.querySelector(".comments");
titleArrow.addEventListener('click', function() {
maintxt.classList.toggle("hidden");
mainArrow.classList.toggle("hidden");
if (comments.classList.contains("hidden")) {;
} else {
comments.classList.toggle("hidden");
};
});
mainArrow.addEventListener("click", function() {
comments.classList.toggle("hidden");
});
});
You have a couple of options here, but the thing they have in common is that when you get a click on a .title-arrow element, you know which .title-arrow was clicked because it's the value of this (and event.currentTarget) in the event callback. From there, you can find the other elements it relates to using various DOM properties and methods, such as closest or the element version of querySelector.
So you could loop through all the .title-arrow elements and hook click on them, and then work it out from there. But I think you probably want to use event delegation instead: Hook click once, on the container for the various .ideanode elements, and then use event.target to figure out what to do.
I assume all those .ideanode elements are in a container, e.g. something like:
<div class="container">
<div class="ideanode">
<div class="ideanodeheader">Need</div>
<div class="content">
<div class="title">
<h3 contenteditable="True" onclick='this.focus();'>Title</h3>
</div>
<i class="fas fa-sort-down title-arrow">v</i>
<div class="maintext">
<textarea placeholder="Text" class="maintextinput"></textarea>
</div>
<i class="fas fa-sort-down maintxt-arrow">v</i>
<div class="comments">
<textarea placeholder="Comments" class="commentsinput"></textarea>
</div>
</div>
</div>
<div class="ideanode">
<div class="ideanodeheader">Need</div>
<div class="content">
<div class="title">
<h3 contenteditable="True" onclick='this.focus();'>Title</h3>
</div>
<i class="fas fa-sort-down title-arrow">v</i>
<div class="maintext">
<textarea placeholder="Text" class="maintextinput"></textarea>
</div>
<i class="fas fa-sort-down maintxt-arrow">v</i>
<div class="comments">
<textarea placeholder="Comments" class="commentsinput"></textarea>
</div>
</div>
</div>
<!-- ...and so on... -->
</div>
So you can do this (see comments):
// A single handler on the container
document.querySelector(".container").addEventListener("click", function(event) {
// Find the arrow that was clicked and the .ideanode it's in, if any
const arrow = event.target.closest(".title-arrow, .maintxt-arrow");
const ideanode = arrow && arrow.closest(".ideanode");
if (!ideanode || !this.contains(ideanode)) {
// Click wasn't on a `.title-arrow` or a `.maintxt-arrow` within an `.ideanode`
return;
}
if (arrow.matches(".title-arrow")) {
// It was a .title-arrow
titleArrowClick.call(arrow, ideanode, event);
} else {
// It was a .maintxt-arrow
mainArrowClick.call(arrow, ideanode, event);
}
});
function titleArrowClick(ideanode, event) {
// Use `querySelector` to look for elements within `.ideanode`
ideanode.querySelector(".maintext").classList.toggle("hidden");
ideanode.querySelector(".maintxt-arrow").classList.toggle("hidden");
const comments = ideanode.querySelector(".comments");
// (Couldn't the following be replaced with `comments.classList.add("hidden");` ?)
if (comments.classList.contains("hidden")){
;
} else {
comments.classList.toggle("hidden");
};
}
function mainArrowClick(ideanode, event) {
ideanode.querySelector(".comments").classList.toggle("hidden");
}
Live Example:
// A single handler on the container
document.querySelector(".container").addEventListener("click", function(event) {
// Find the arrow that was clicked and the .ideanode it's in, if any
const arrow = event.target.closest(".title-arrow, .maintxt-arrow");
const ideanode = arrow && arrow.closest(".ideanode");
if (!ideanode || !this.contains(ideanode)) {
// Click wasn't on a `.title-arrow` or a `.maintxt-arrow` within an `.ideanode`
return;
}
if (arrow.matches(".title-arrow")) {
// It was a .title-arrow
titleArrowClick.call(arrow, ideanode, event);
} else {
// It was a .maintxt-arrow
mainArrowClick.call(arrow, ideanode, event);
}
});
function titleArrowClick(ideanode, event) {
// Use `querySelector` to look for elements within `.ideanode`
ideanode.querySelector(".maintext").classList.toggle("hidden");
ideanode.querySelector(".maintxt-arrow").classList.toggle("hidden");
const comments = ideanode.querySelector(".comments");
// (Couldn't the following be replaced with `comments.classList.add("hidden");` ?)
if (comments.classList.contains("hidden")){
;
} else {
comments.classList.toggle("hidden");
};
}
function mainArrowClick(ideanode, event) {
ideanode.querySelector(".comments").classList.toggle("hidden");
}
.hidden {
display: none;
}
<div class="container">
<div class="ideanode">
<div class="ideanodeheader">Need</div>
<div class="content">
<div class="title">
<h3 contenteditable="True" onclick='this.focus();'>Title</h3>
</div>
<i class="fas fa-sort-down title-arrow">v</i>
<div class="maintext">
<textarea placeholder="Text" class="maintextinput"></textarea>
</div>
<i class="fas fa-sort-down maintxt-arrow">v</i>
<div class="comments">
<textarea placeholder="Comments" class="commentsinput"></textarea>
</div>
</div>
</div>
<div class="ideanode">
<div class="ideanodeheader">Need</div>
<div class="content">
<div class="title">
<h3 contenteditable="True" onclick='this.focus();'>Title</h3>
</div>
<i class="fas fa-sort-down title-arrow">v</i>
<div class="maintext">
<textarea placeholder="Text" class="maintextinput"></textarea>
</div>
<i class="fas fa-sort-down maintxt-arrow">v</i>
<div class="comments">
<textarea placeholder="Comments" class="commentsinput"></textarea>
</div>
</div>
</div>
<!-- ...and so on... -->
</div>
Here's a suggestion, out of the many possibilities to tackle such a thing:
You start with the most basic array of classnames references and build you way into an Object with keys & values which values represent DOM nodes.
For the events part, you should very much use event-delegation so you won't need to bind an event for each and every card. Just make sure they have the same parent node, and bind your events there.
// preparation
var elms = ['maintext', 'title-arrow', 'maintxt-arrow'].reduce((acc, name) => {
acc[name] = document.querySelector(`.${name}`)
return acc
}, {}
)
// iterating each "variable"
for( let elm in elms )
console.log(elm)
<div class='maintext'></div>
<div class='title-arrow'></div>
<div class='maintxt-arrow'></div>

How to create collapse expand list in react

I try to create a collapse and expand side menu in React (v 16.5) with the following criteria -
On page load first item (Circulars) will be in expanded view. Any of one item can expand at a time, like, if user clicks on the second item (Specifications), the first item will collapse. I also want some CSS animation during collapse/expand transaction , like smoothly down/up the body section of each item and change the arrow icons. My approach is to add/remove a CSS class on each item (sidebar-nav-menu-item) dynamically, like -
sidebar-nav-menu-item item-active
So, when a item was in expanded view it should be like above class and remove item-active when its in collapse view. By default, the body divs (sidebar-nav-menu-item-body) should be hidden through CSS when the item in a collapse mode.
import React, { Component } from 'react';
className SidebarNavs extends React.Component{
constructor(props) {
super(props);
}
render() {
return(
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<div className="sidebar-nav-menu-item" data-id="circulars">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Circulars</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className="sidebar-nav-menu-item" data-id="specifications">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Specifications</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className="sidebar-nav-menu-item" data-id="wo">
<div className="sidebar-nav-menu-item-head" onClick={this.handleExpandCollaps}>
<div className="sidebar-nav-menu-item-head-title">Work Orders</div>
<div className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true"></i>
</div>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
</div>
</div>
)
}
}
export default SidebarNavs;
CSS:
.sidebar-nav-menu-item{
display:block;
}
.sidebar-nav-menu-item-body{
display:none;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body{
display:block;
}
You should use a state variable to show your collapsiable item active / in-active.
I modified your code a bit to fit it into your requirements.
class App extends Component {
constructor() {
super();
this.state = {
activeCollapse: 'circulars'
};
}
handleExpandCollaps = (name) => {
if (this.state.activeCollapse === name) {
this.setState({ activeCollapse: '' })
} else {
this.setState({ activeCollapse: name })
}
}
moreInfoClick = (e) => {
e.stopPropagation();
console.log("clicked");
}
render() {
return (
<div>
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "circulars" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("circulars")} data-id="circulars" >
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Circulars</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "specifications" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("specifications")} data-id="specifications">
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Specifications</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
<div className={`sidebar-nav-menu-item ${this.state.activeCollapse === "wo" ? 'item-active' : ''}`} onClick={() => this.handleExpandCollaps("wo")} data-id="wo">
<div className="sidebar-nav-menu-item-head">
<span className="sidebar-nav-menu-item-head-title">Work Orders</span>
<span className="sidebar-nav-menu-item-head-help">
<button type="button" className="btn-help" onClick={this.moreInfoClick}>View more info</button>
</span>
</div>
<div className="sidebar-nav-menu-item-body">BODY CONTENT HERE</div>
</div>
</div>
</div>
</div>
);
}
}
Note: I have used CSS for font-awesome icons. Hope you have added font-awesome
Demo
To do that I would use React.useState, since its a small state to control and to animate I would use CSS:
The component would look like this:
function SidebarNavs() {
const [activeItem, setActiveItem] = React.useState(1);
return (
<div className="sidebar-nav">
<div className="sidebar-nav-menu">
<SidebarItem
title="Circulars"
setActiveItem={setActiveItem}
index={1}
activeItem={activeItem}
>
Sidebar Content Here
</SidebarItem>
<SidebarItem
title="Specifications"
setActiveItem={setActiveItem}
index={2}
activeItem={activeItem}
>
Sidebar Content Here
</SidebarItem>
<SidebarItem
title="Specifications"
setActiveItem={setActiveItem}
index={3}
activeItem={activeItem}
>
Work Orders
</SidebarItem>
</div>
</div>
);
}
function SidebarItem({ title, children, setActiveItem, activeItem, index }) {
const expanded = activeItem === index;
const cls = "sidebar-nav-menu-item " + (expanded ? "item-active" : "");
return (
<div className={cls}>
<div className="sidebar-nav-menu-item-head">
<div className="sidebar-nav-menu-item-head-title">{title}</div>
<div className="sidebar-nav-menu-item-head-help">
<button
type="button"
className="btn-help"
onClick={() => setActiveItem(index)}
>
View more info
</button>
</div>
<div className="sidebar-nav-menu-item-head-icon">
<i className="fa fa-caret-down" aria-hidden="true" />
</div>
</div>
<div className="sidebar-nav-menu-item-body">{children}</div>
</div>
);
}
The CSS would look like this:
.sidebar-nav-menu-item {
border: 1px solid #CCC;
margin-bottom: 20px;
}
.sidebar-nav-menu-item .sidebar-nav-menu-item-body {
overflow: hidden;
max-height: 0;
transition: all linear 0.5s;
}
.sidebar-nav-menu-item.item-active .sidebar-nav-menu-item-body {
max-height: 100px;
transition: all linear 0.5s 0.3s;
}

Jquery Scroll Top Not Responding

I have a span called span#about, which when I click on it, I want to scroll down the page to the top of a div called div#perthImage
When I click on the span, it displays 'scroll initiate' so I know that the click is being registered however it does not scroll. Can somebody please assist me.
$(document).ready(function () {
$("span#about").click(function () {
console.log('scroll initiate')
$('html,body').animate({ scrollTop: $('div#perthImage').offset().top }, 200);
});
});
My HTML code:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.js"></script>
<link rel="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css">
<div id='nav' class="nav">
<div class='row'>
<div class = 'col-xs-12' id = 'aboutBtn'>
<span id = 'about'>About Us</span>
</div>
<div class = 'col-xs-12' id = 'citiesBtn'>
<span id = 'cities'>Cities <i class="fa fa-angle-down" aria-hidden="true"></i></span>
<div id = 'oncityhover'>
<div class = 'row' id = 'citiesDropdown'>
<div class = 'menuItem col-xs-12' id = 'perthScroll'>
Perth City
</div>
</div>
</div>
</div>
</div>
</div>
<div id = 'homeLogo'>
<center>
<img id='logo' src="images/CulturedLogo.png">
</div>
<br><br><br><br><br><br><br><br><br><br><br><br><br><br>
<br><br><br><br><br><br><br><br><br><br><br>
<div class = 'cities row'>
<div class = 'locImage col-md-6' id = 'perthImage' title = 'Perth City' alt = 'Perth City'>
<center>
<h4 id = 'perth'>Perth City</h4>
</center>
</div>
</div>

Categories

Resources