JavaScript active class on menu items - javascript

I have the following html and JavaScript to add an active class called is-active to a menu item onClick. When you click on another item it gets removed from the previous one and gets added to the current one. My code is as follows which I got from W3Schools. I wanted to know if there is a better and more modern way of writing the JavaScript. (NO jQuery) only plain vanilla JavaScript
HTML:
<div class="our-clients__categories">
<h4>Technology</h4>
<h4>Retail</h4>
<h4>Finance</h4>
<h4>Consumer</h4>
<h4>Hospitality</h4>
</div>
JavaScript:
export default function () {
const clientCategoryButtons = document.querySelectorAll('.our-clients__categories--category')
clientCategoryButtons.forEach((categorybtn) => {
categorybtn.addEventListener('click', function (e) {
e.preventDefault()
const current = document.getElementsByClassName('is-active')
current[0].className = current[0].className.replace('is-active', '')
this.className += ' is-active'
})
})
}
Any and all help is appreciated! :)

Related

AddEventListener event fires only once

I am using Django templating engine and JavaScript. My HTML looks like this
<p class="content-card__address">{{ z.formatted_address|truncatewords:6 }}</p>
<div class="content-card-inner">
<p class="content-card__review">Отзывы ({{ z.post_relate.all.count }})</p>
<p class="content-card__phone">{{ z.international_phone_number }}</p>
<div class="div-shaddow"></div>
<p class="content-card__text">Показать</p>
</div>
Cards with text to be generated on the backend using a template engine. My JavaScript code only works on the first card and I need it to work on all cards. With JavaScript I add a class to the div elements. Here is my JavaScript
let call = document.querySelector('.content-card__text');
let divShadow = document.querySelector('.div-shaddow');
call.addEventListener('click', clickCall)
function clickCall() {
call.classList.add('visually-hidden');
divShadow.classList.add('visually-hidden');
}
This code returns you the first element in the DOM and you add click handlers only for it
document.querySelector('.content-card__text')
It will work for you:
const buttons = document.querySelectorAll('.content-card__text');
buttons.forEach(button => {
button.addEventListener('click', clickCall);
});
But please also note that you need to take this into account when working with .divShadow if this element is not alone on the page
Update: example based on your comment
const buttons = document.querySelectorAll('.content-card__text');
const divShadow = document.querySelectorAll('.div-shaddow');
buttons.forEach((button, index) => {
button.addEventListener('click', () => clickCall(index));
});
function clickCall(index) {
buttons[index].classList.add('visually-hidden');
divShadow[index].classList.add('visually-hidden');
}

Angular 12.1 add html element using typescript

I'm learning angular via youtube, but I'm trying to do something new, and I'm getting an error on that, my code is attached below, help me out.
I want to setAttribute like this div.setAttribute('(click)',"popUp($event)"); but I got error.
TypeScript
export class AppComponent {
createEl(){
console.time("timer");
for (let i = 0; i < 10; i++) {
let div = document.createElement("div");
div.textContent = `Hello, World! ${i}`;
div.setAttribute('(click)',"popUp($event)");
document.getElementById('divEl')?.appendChild(div);
};
console.timeEnd("timer");
}
HTML
<div id="divEl"></div>
<button (click)="createEl()">click me</button>
Error
This is not really the angular way of doing things. Try to avoid operations on document such as document.createElement.
A better way to achieve this would be to define what the repeating element would look like in the template and drive it from an array. That way we can keep the template doing display and the typescript doing processing, and Angular handling everything in between.
HTML
<div id="divEl">
<div *ngFor="let row of rows; index as i;" (click)="popUp($event)">
Hello, World! {{i}}
</div>
</div>
<button (click)="createEl()">click me</button>
Typescript
export class AppComponent {
rows: unknown[] = [];
createEl():void {
this.rows.push('something');
}
popUp(event:Event):void {}
}
More reading on loops: https://angular.io/api/common/NgForOf
That's right check below.
div.addEventListener('click', (e) => {
this.popUp(e);
});
Problem is you are trying to do angular stuff with pure javascript.
<div (click)="method()"> is angular.
In javascript you'd do someting like this <button onclick="myFunction()">Click me</button>
Other options are to use event handlers https://www.w3schools.com/js/js_htmldom_eventlistener.asp
Anyhow, angular doesn't recommend changes the DOM because then it won't recognize those changes. Here are multiple examples ho to properly change the dom
Correct way to do DOM Manipulation in Angular 2+
https://medium.com/#sardanalokesh/understanding-dom-manipulation-in-angular-2b0016a4ee5d
`
You can set the click event as shown below instead of using setAttribute
div.addEventListener('click', (e) => {
this.popUp(e);
});
(click) is not an html attribute, it is Angular event binding syntax
This syntax consists of a target event name within parentheses to the left of an equal sign, and a quoted template statement to the right.
You cannot use that with JavaScript. Use
div.onclick = popUp;
export class AppComponent {
createEl(){
console.time("timer");
for (let i = 0; i < 10; i++) {
let div = document.createElement("div");
div.textContent = `Hello, World! ${i}`;
div.addEventListener('click', (e) => {
this.popUp(e);
});
document.getElementById('divEl')?.appendChild(div);
};
console.timeEnd("timer");
}

How to get MDCMenu instance by element?

Let assume that I have a lot of html elements need to use MDCMenu. I don't want to init them one by one, so I init all of them with the code below:
html:
<button class="my-menu-toggle" data-toggle="mdc-menu" data-target="#my-menu">Menu Toggle</button>
<div class="mdc-menu" id="my-menu">
</div>
js:
document.querySelectorAll('[data-toggle="mdc-menu"]').forEach(toggleEl => {
let menuEl = document.querySelector(toggleEl.dataset.target);
let menu = new MDCMenu(menuEl);
toggleEl.addEventListener('click', (e) => {
menu.open = !menu.open;
});
// maybe I should do this, just wondering that if MDC already do same thing that I haven't figure out.
menuEl.MDCMenu = menu;
});
then I want to do somethings with one of menu, how can I get the MDCMenu instance of the element?

Local storage not applying to remember the last user input for the class toggle

Local storage value saved but how to use the saved local storage used. I tried creating a function apply but the class is not applying
document.querySelectorAll("#abc, #xyz").forEach(btn => {
btn.addEventListener("click", () => {
const e = document.querySelectorAll(".dog,.cat, #abc, #xyz");
e.forEach(el => {
el.classList.toggle("red");
var xyz = document.getElementById("xyz").className;
localStorage.setItem("vovo", xyz);
});
});
})
function apply(){
var xy = localStorage.getItem('vovo');
if (xy) {
var single = document.querySelectorAll(".dog,.cat, #abc, #xyz");
single.forEach(el => {
el.classList.add(xy);
});
}
};
The functionality logic :
when a button is pressed we check if it has the red class (as all the elements, buttons and the other divs, will receive the red class at some point).
if it has that class then it will be toggled (it will be removed from all the buttons and the other divs) thus the localStorage will store something like "No the elements don't have the red class".
if it doesn't has it the it will be toggled (it will be added to all the buttons and the other divs) thus the localStorage will store something like "Yes the elements have the red class".
basically, the localStorage will store '0' if the class isn't applied, '1' if the class is applied.
now, when the page gets refreshed, we check if the localStorage item that stores the red class' state is there (not null) and has the value of '1' then apply that class to all the elements (the buttons and the other divs).
remove the item that holds the red class' state from the localStorage.
Here's the update JavaScript code :
Sorry for everyone as I can't make a live demo using SO's snippets as the localStorage can't be reached because the code is sandboxed.
Anyway, here's a CodePen demo illustrating the required functionality.
const els = document.querySelectorAll('.dog,.cat, #abc, #xyz');
/** when the page finishes loading **/
document.addEventListener('DOMContentLoaded', () => {
/** check if the 'red' class was applied **/
applied = window.localStorage.getItem('class-applied') === '0' ? false:true;
/** remove "class-applied" item from the localStorage **/
window.localStorage.removeItem('class-applied');
/** if the "red" class was applied just apply it on document load **/
applied && els.forEach(el => el.classList.add('red'));
});
els.forEach(btn => {
btn.addEventListener('click', () => {
/** store the "red" class' state : '1' means it is applied, '0' means it isn't apllied **/
window.localStorage.setItem(
'class-applied',
!btn.classList.contains('red') ? '1' : '0'
);
els.forEach(el => el.classList.toggle('red'));
});
});
The above code should be placed just before </body>.
just use the following syntax:
localStorage.setItem('myLocalStorageVariable', 'myValue');
If you want to read the value instead:
localStorage.getItem('myLocalStorageVariable')

Getting HTML element by Id and switch its CSS through React

I have some files that load into my react components, which have HTML code.
As it is now, the pure HTML code renders just fine, however there is some 'hidden' code that appears whenever you click certain buttons in other parts of the application or on the text above (think of it like panels that expand when you click on it).
The HTML is hidden just using the good old <div id="someId" style="display:none">.
Anyway I am trying to get the correct panel to expand upon clicking their respective buttons.
So in theory, what I need to do is find the element by id, and switch it's display to block whenever needed, and then switch it back when the parent is clicked again.
Unfortunately I have no idea how to do this and so far have gotten nowhere. As it is now, I have access to the component's ids. What I want to know is how in the world can I access that and get to change whatever is rendering?
Create your function:
function element_do(my_element, what_to_do) {
document.getElementById(my_element).style.display = what_to_do;
}
and latter in code you can append wherever you want through javascript onclick or not depends what do you need:
element_do("someId", "none"); // to hide
element_do("someId", "block"); // to show
or create yourself toggle:
function toggle_element(element_id) {
var element = document.getElementById(element_id);
element.style.display = (element.style.display != 'none' ? 'none' : 'block' );
}
// and you can just call it
<button onClick="toggle_element('some_id')">toggle some element</button>
The react way to do it would be with states. Assuming that you know how to use states I'd do something like this:
class ShowHide extends React.Component {
constructor() {
super();
this.state = {myState: true};
this.onClick = this.onClick.bind(this)
}
onClick() {
this.setState({myState: !this.state.myState}) //set the opposite of true/false
}
render() {
const style = {myState ? "display: none" : "display:block"} //if myState is true/false it will set the style
return (<div>
<button onClick={this.onClick}>Click me to hide/show me </button>
<div id="myDiv" style={style}> Here you will hide/show div on click </div>
</div>)
}
}

Categories

Resources