I dont know javascript and need assistance with a html navbar function - javascript

So here is the code
What is wrong with it?
it should trigger a block if I click on this block (burger) the nav-active function should be triggered.
Here is the JS part:
const navSlide = () => {
const burger = document.querySelector('.burger');
const nav = document.querySelectorAll('.nav-links');
burger.addEventListener('click',()=>{
nav.classList.toggle('nav-active');
});
}
navSlide();
My CSS
.nav-active {
transform: translate(0%);
}
I added the javascript file at the end of my html file:
<script src="../static/java_file.js"></script>

Since document.querySelectorAll() returns a NodeList object, you need to iterate every nav link using .forEach() or using a loop like for:
const navSlide = () => {
const burger = document.querySelector('.burger');
const nav = document.querySelectorAll('.nav-links');
burger.addEventListener('click',() => {
// nav is a collection, so we use .forEach()
nav.forEach(n => n.classList.toggle('nav-active'));
});
}
navSlide();
Small tipps: Use your in-browser developer tools (opened usually by pressing F12) to check for the exact exception messages. Also: don't call your javascript-file 'java_file.js'.

Related

ReactJS - Print specific elements in the DOM

I am using ReactJS on an App and currently need to be able to print some elements from the page on user's request (click on a button).
I chose to use the CSS media-query type print (#media print) to be able to check if an element should be printed, based on a selector that could be from a class or attribute on an Element. The strategy would be to hide everything but those "printable" elements with a stylesheet looking like:
#media print {
*:not([data-print]) {
display: none;
}
}
However, for this to work I need to also add the chosen print selector (here the attribute data-print) on every parent element each printable element has.
To do that here's what I've tried so far:
export default function PrintButton() {
useEffect(() => {
const handleBeforePrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
let element = printableElement;
while (element.parentElement) {
element.setAttribute("data-print", "");
element = element.parentElement;
}
element.setAttribute("data-print", "");
}
});
};
const handleAfterPrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
let element = printableElement;
while (element.parentElement) {
element.removeAttribute("data-print");
element = element.parentElement;
}
element.removeAttribute("data-print");
}
});
};
window.addEventListener("beforeprint", handleBeforePrint);
window.addEventListener("afterprint", handleAfterPrint);
return () => {
window.removeEventListener("beforeprint", handleBeforePrint);
window.removeEventListener("afterprint", handleAfterPrint);
};
}, []);
return <button onClick={() => window.print()}>Print</button>;
}
With printNodeSelectors being a const Array of string selectors.
Unfortunately it seems that React ditch out all my dirty DOM modification right after I do them 😭
I'd like to find a way to achieve this without having to manually put everywhere in the app who should be printable, while working on a React App, would someone knows how to do that? 🙏🏼
Just CSS should be enough to hide all Elements which do not have the data-print attribute AND which do not have such Element in their descendants.
Use the :has CSS pseudo-class (in combination with :not one) to express that 2nd condition (selector on descendants):
#media print {
*:not([data-print]):not(:has([data-print])) {
display: none;
}
}
Caution: ancestors of Elements with data-print attribute would not match, hence their text nodes (not wrapped by a tag) would not be hidden when printing:
<div>
<span>should not print</span>
<span data-print>but this should</span>
Caution: text node without tag may be printed...
</div>
Demo: https://jsfiddle.net/6x34ad50/1/ (you can launch the print preview browser feature to see the effect, or rely on the coloring)
Similar but just coloring to directly see the effect:
*:not([data-print]):not(:has([data-print])) {
color: red;
}
<div>
<span>should not print (be colored in red)</span>
<span data-print>but this should</span>
Caution: text node without tag may be printed...
</div>
After some thoughts, tries and errors it appears that even though I managed to put the attribute selector on the parents I completely missed the children of the elements I wanted to print! (React wasn't at all removing the attributes from a mysterious render cycle in the end)
Here's a now functioning Component:
export default function PrintButton() {
useEffect(() => {
const handleBeforePrint = () => {
printNodeSelectors.forEach((selector) => {
const printableElement = document.querySelector(selector);
if (printableElement != null) {
const elements: Element[] = [];
// we need to give all parents and children a data-print attribute for them to be displayed on print
const addParents = (element: Element) => {
if (element.parentElement) {
elements.push(element.parentElement);
addParents(element.parentElement);
}
};
addParents(printableElement);
const addChildrens = (element: Element) => {
elements.push(element);
Array.from(element.children).forEach(addChildrens);
};
addChildrens(printableElement);
elements.forEach((element) => element.setAttribute("data-print", ""));
}
});
};
const handleAfterPrint = () => {
document.querySelectorAll("[data-print]").forEach((element) => element.removeAttribute("data-print"));
};
window.addEventListener("beforeprint", handleBeforePrint);
window.addEventListener("afterprint", handleAfterPrint);
return () => {
window.removeEventListener("beforeprint", handleBeforePrint);
window.removeEventListener("afterprint", handleAfterPrint);
};
}, []);
return <button onClick={() => window.print()}>Print</button>;
}
I usually don't like messing with the DOM while using React but here it allows me to keep everything in the component without having to modify anything else around (though I'd agree that those printNodeSelectors need to be chosen from outside and aren't dynamic at the moment)

Event Listeners only working on page refresh

I know there are a few similar questions but I have waded through them without any luck.
My event listeners (both "click") work great, but only once, after a page refresh. It is just code to slide the hidden nav menu out for a mobile device screen.
Here is the JS code:
/*Menu responsive code*/
const hamburger = document.getElementById('menuIcon');
const closeMenu = document.getElementById('closeNav');
const navUL = document.getElementById('navUL');
hamburger.addEventListener('click', () =>{
navUL.classList.toggle('show');
});
closeMenu.addEventListener('click', () =>{
navUL.classList.toggle('hidden');
})
Again, this problem has to do with the javascript, not the CSS/HTML since it works great that one time (unless I am completely wrong). Also new to javascript.
hamburger and closeMenu are my ionicons used/the buttons. the show and hidden classes just translateX between 0% and 100%.
In your case with 2 classes you should remove the other class when you are adding the new one.
if you are using only one class you can use toogle but with 2 classes yo have to use add and remove together.
exemple here :
const hamburger = document.getElementById('menuIcon');
const closeMenu = document.getElementById('closeNav');
const navUL = document.getElementById('navUL');
hamburger.addEventListener('click', () =>{
navUL.classList.add('red');
navUL.classList.remove('blue');
});
closeMenu.addEventListener('click', () =>{
navUL.classList.add('blue');
navUL.classList.remove('red');
})
https://codepen.io/pen/?editors=1111

Problem highlighting sidebar navigation items on scroll in React with IntersectionObserver

I'm working on a react project and I want to highlight the sidebar nav list when the corresponding section is visible while scrolling, and I used useEffect and IntersectionObserver for that and add an active class to the sidebar nav item with the code below.
The problem is that some of the sections are not 100% height of the viewport, causing multiple sidebar nav list items to highlight simultaneously and I do not want that. I want only a single nav item to have the active class.
useEffect(() => {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
const id = entry.target.getAttribute('id');
if (entry.isIntersecting) {
document
.querySelector(`.sidebarList li a[href="#${id}"]`)
.classList.add('active');
} else {
document
.querySelector(`.sidebarList li a[href="#${id}"]`)
.classList.remove('active');
}
});
});
document.querySelectorAll('section[id]').forEach((section) => {
observer.observe(section);
});
return () => observer.disconnect();
});
I think you will have to use useRef instead of using your querySelector.

In React how to prevent to use function onClick if width of web browser is less than

On mobile device my function onClick has two roles (open/close Menu and status changing of my Hamburger menu. However both states working in the background if my navigation is set up for desktops screens also. I would like to prevent to use function onClick if width of web browser is less than 757px;
Here is my code:
const [showMobileMenu, setShowMobileMenu] = useState(false);
const [navBarStatus, setNavBarStatus] = useState(false);
const onClick = () => {
setShowMobileMenu(!showMobileMenu);
setNavBarStatus(!navBarStatus);
}
I have tried to add additionall condition to function onClick but it is not the right way :(
const onClick = () => {
if (innerWidth < 757)
setShowMobileMenu(!showMobileMenu);
setNavBarStatus(!navBarStatus);
}
Edit:
Problem solved. Object window was missing in syntax
const onClick = () =>{
if (window.innerWidth<757) {
setShowMobileMenu(!showMobileMenu);
setNavBarStatus(!navBarStatus);
}
}
Try window.matchMedia("(max-width: 757px)") in your on click function . If you are using material-ui use material-ui/withWidth
or use npm packages like media-query-react
hide the button for width less than 757px and show another button for mobile device and set separate onClick functions for them:
#media (max-width: 757px) {
.mobile-button {
display:inline;
}
.desktop-button {
display:none;
}
}
#media (min-width: 757px) {
.mobile-button {
display:none;
}
.desktop-button {
display:inline;
}
}

how to hang a handler on a collection of elements that returns to the querySelectorAll function?

I want to make a drop-down menu when clicking on itemDropDown, only the first one works, and the rest does not, querySelectorAll gives an error, what am I doing wrong? I have attached jsfidle so you can help, don'ts please pay attention to scss. http://jsfiddle.net/rhy7pv1f/46/
const itemDropDown = document.querySelectorAll('.menu__list-item.drop__down'),
itemList = document.querySelector('.drop__down-list');
itemDropDown.forEach((buttonItem) => {
buttonItem.addEventListener('click', () => {
itemList.classList.toggle('active');
})
})
All your handlers toggle the class of the one itemList you create at the start.
You need to target the relevant list for each button.
const itemDropDown = document.querySelectorAll('.menu__list-item.drop__down');
itemDropDown.forEach((buttonItem) => {
const relatedDropdownList = buttonItem.querySelector('.drop__down-list');
const dropDownLink = buttonItem.querySelector('.menu__list-link');
dropDownLink.addEventListener('click', () => {
relatedDropdownList.classList.toggle('active');
});
})
Updated fiddle at http://jsfiddle.net/gaby/k7b0m95a/5/

Categories