Trying to get this function to work properly. The toggleNav works on it's own. I want to apply different Open/close functionality based on screen size. the navigation opens and closes based on mouse events, so the code needs to continuously run at each screen size.
let opened = false; // set the nav as closed by default
if ($(window).width() > 720) {
function toggleNav() {
if(!opened) { // if opened is false (ie nav is closed), open the nav
openNav()
} else { // else, if opened is ture (ie nav is open), close the nav
closeNav();
}
opened = !opened; // negate boolean to get opposite (t to f, and f to t)
}
}else{
function toggleNav2() {
if(!opened) {
openNav2()
} else {
closeNav2();
}
opened = !opened;
}
}
function openNav() {
$('#myTopnav').addClass('openHeight').removeClass('closeHeight');
$('#main').addClass('openMain').removeClass('closeMain');
}
function closeNav() {
$('#myTopnav').removeClass('openHeight').addClass('closeHeight');
$('#main').removeClass('openMain').addClass('closeMain');
}
function openNav2() {
$('#main').addClass('openMain').removeClass('closeMain');
}
function closeNav2() {
$('#main').removeClass('openMain').addClass('closeMain');
}
I'm guessing that you're asking how to make this work in the event that the user resizes their view.
I would recommend rearranging your styles so that there is one master class that controls this feature, on a single parent element (e.g.: body), which you can then toggle on and off via javascript. In this way, all style changes (including those caused by user view size changes) are handled by CSS and javascript only has to manage a single element.
You don't provide enough of your code to rework what you're doing but, as an example (click "expand snippet" and resize your browser window as needed to experiment with the width):
$('button').on('click', e => {
$('body').toggleClass('off');
});
.top:after {
display:block;
content:"on";
color: #080;
}
.bottom:after {
display:block;
content:"on";
color: #080;
}
/* overrides for "off" state */
.off .top:after {
content:"off";
color: #800;
}
/* only override css if view width > 720px */
#media (min-width:721px) {
.off .bottom:after {
content:"off";
color: #800;
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button type="button">toggle</button>
<div class="top"></div>
<div class="bottom"></div>
Related
I am trying to add dark-mode to the website. When someone clicks the dark mode button it will add additional CSS classes and remove them when the button is clicked again.
I know I can do it easily with toggleClass but I don't want to disable this automatically when the page refreshes or some other page is opened on the website.
I have been playing with sessionStorage but unable to succeed so far I have come up with this code here:
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
$('.darkmode-button').click(function() {
if (sessionStorage.getItem('darkmode', 'true')) {
$('.header-wrap').removeClass('dark-header');
sessionStorage.setItem('darkmode', 'false');
}
if (sessionStorage.getItem('darkmode', 'false')) {
$('.header-wrap').addClass('dark-header');
sessionStorage.setItem('darkmode', 'true');
}
});
function changeText() {
var x = document.getElementById("dmbutton");
if (x.innerHTML === "Dark Mode") {
x.innerHTML = "Light Mode";
} else {
x.innerHTML = "Dark Mode";
}
}
.header-wrap {
color: black;
}
.dark-header {
color: white;
background-color: black;
}
Can someone please share a working example of how it can be achieved?
I already created a question before but it was marked duplicate with this answer. I read it all but still could not figure it out.
To do what you require simply set a single class on a parent element, the body would work well in this case, to indicate when dark mode has been turned on. You can then use this class in all the relevant selectors in your CSS to update the UI.
Regarding the session storage logic, set a boolean flag when the dark mode is updated when the button is clicked and set the class on the body based on the session storage flag when the page loads.
Putting it all together would look something like this:
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
let body = document.body;
let dmButton = document.querySelector('#dmbutton');
dmButton.addEventListener('click', e => {
body.classList.toggle('dark');
sessionStorage.setItem('darkmode', body.classList.contains('dark'));
e.target.textContent = e.target.textContent.trim() === 'Dark Mode' ? 'Light Mode' : 'Dark Mode';
});
let darkModeEnabled = JSON.parse(sessionStorage.getItem('darkmode')); // boolean type coercion
if (darkModeEnabled) {
body.classList.add('dark');
dmButton.textContent = 'Light Mode';
}
.header-wrap {
color: black;
}
body.dark {
background-color: #666;
}
body.dark .header-wrap {
color: white;
background-color: black;
}
Here's a working example in a jsFiddle, as SO snippets are sandboxed and disallow local/session storage access.
I don't know the logic of this code but it works for me which I found from this solution thanks to Stackoverflow
Dark Mode
<div class="header-wrap">
Testing toggle with session
</div>
var $dark = $('.header-wrap')
if (localStorage.getItem('darkmode') === 'true') {
$dark.addClass('dark-header');
}
$('.darkmode-button').click(function() {
$dark.toggleClass('dark-header');
localStorage.setItem('darkmode', $dark.hasClass('dark-header'));
});
.header-wrap {
color: black;
}
.dark-header {
color: white;
background-color: black;
Given some text:
<div>text</div>
I would like to detect when the computed CSS property color changes for this div.
There could be a number of css queries that would change its color, like media queries:
div {
color: black;
#media (prefers-color-scheme: dark) {
color: white;
}
#media screen and (max-width: 992px) {
background-color: blue;
}
}
Or perhaps a class applied to a parent:
div {
}
body.black {
color: white;
}
How can I, using Javascript, observe this change of computed style?
I think we can get part way there at least by using mutation observer on the whole html, that will detect things like change to the attributes of anything, which may or may not influence the color on our to be 'observed' element, and by listening for a resize event which will at least catch common media query changes. This snippet just alerts the color but of course in practice you'll want to remember the previous color (or whatever you are interested in) and check to see if it is different rather than alerting it.
const observed = document.querySelector('.observed');
const html = document.querySelector("html");
let style = window.getComputedStyle(observed);
// NOTE: from MDN: The returned style is a live CSSStyleDeclaration object, which updates automatically when the element's styles are changed.
const observer = new MutationObserver(function(mutations) {
alert('a mutation observed');
mutations.forEach(function(mutation) {
alert(style.color);
});
});
function look() {
alert(style.color);
}
observer.observe(html, {
attributes: true,
subtree: true,
childList: true
});
window.onresize = look;
.observed {
width: 50vmin;
height: 50vmin;
color: red;
}
#media (min-width: 1024px) {
.observed {
color: blue;
}
}
#media (max-width: 700px) {
.observed {
color: gold;
}
<div class="observed">See this color changing.<br> Either click a button or resize the viewport.</div>
<button onclick="observed.style.color = 'purple';">Click for purple</button>
<button onclick="observed.style.color = 'magenta';">Click for magenta</button>
<button onclick="observed.style.color = 'cyan';">Click for cyan</button>
What I don't know is how many other things might influence the setting - I see no way of finding out when the thing is print rather than screen for example. Hopefully someone will be able to fill in any gaps.
I wrote a small program that detects change in a certain CSS property, in getComputedStyle.
Note: Using too many observeChange() may cause performance issues.
let div = document.querySelector("div")
function observeChange(elem, prop, callback) {
var styles = convertObject(getComputedStyle(elem));
setInterval(() => {
let newStyle = convertObject(getComputedStyle(elem));
if (styles[prop] !== newStyle[prop]) { //Check if styles are different or not
callback(prop, styles[prop], newStyle[prop]);
styles = newStyle; //Set new styles to previous object
}
},
500); //You can change the delay
}
//Callback function
function callback(prop, old, newVal) {
console.log(prop + " changed from " + old + " to " + newVal);
}
observeChange(div, "color", callback)
//Convert CSS2Properties object to a normal object
function convertObject(obj) {
let object = {}
for (let i of obj) {
object[i] = obj[i]
}
return object
}
div {
color: green;
}
input:checked+div {
color: red;
}
<input type="checkbox">
<div>Hello World</div>
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;
}
}
I want to use javaScript to recognize when a sidebar is classed "active" or not. I'm using bootstraps' sidebar toggle button, which when clicked, assigns a class of "active" to the sidebar.
<button type="button" id="sidebarCollapse" class="btn btn-info" style="font-family:'Poppins'; position:absolute; z-index:9; margin-left:7vh; margin-top:2vh;font-size: 1.5em">
<span class="glyphicon glyphicon-filter"></span> Filter
</button>
The CSS:
#sidebar {
background: #202020;
color: #fff;
display:inline-block;
}
#sidebar.active {
margin-left: -250px;
}
And, the JS:
//Check to see whether sidebar has class 'active'
var sideBar = document.getElementById('sidebar')
console.log(sideBar.className)
if (sideBar.className == ('active')){
console.log('active')
}
else (console.log('not active'))
To be clear, the active class is only assigned when the sidebarCollapse button is clicked, and the active class is removed when the button is clicked again. The above code doesn't work. It only logs 'not active', even when the sidebar is clearly classed 'active' and is visible. I want it to dynamically read the status of the sidebar (either classed active, or not active).
var sideBar = document.getElementById('sidebar');
console.log(sideBar.className)
if (sideBar.classList.contains('active')){
console.log('active')
}
else (console.log('not active'))
Here's pictures of the HTML, showing the two states of the sidebar (active/not active):
You code should work. There are 2 reasons why your code is always showing 'not active'
Your code is executed on page load
You are fetching the sidebar div before sidebar got opened and the dom object is not updated later.
Move your code to a function and call that function when ever you need to check.
Sample code below.
function isSidebarOpen() {
var sideBar = document.getElementById('sidebar');
//console.log(sideBar.classList)
if (sideBar.classList.contains('active')) {
console.log('active')
} else(console.log('not active'))
}
<div id="sidebar" class="active">
test
<button onclick='isSidebarOpen()'>
Check</button>
</div>
Use MutationObserver.
Add the code below to observe the change:
const targetNode = document.getElementById('sidebarCollapse'); //listen to the sidebar
const config = { attributes: true }; //listen for changes in attributes
const callback = function(mutationsList, observer) {
for(let mutation of mutationsList) {
if (mutation.type === 'attributes') {
if (targetNode.classList.contains('active')){
console.log('active');
}
}
}
};
const observer = new MutationObserver(callback); //construct observer
observer.observe(targetNode, config); //start observing
A working pen here.
I want an overlay to show up when I click a search icon.
I managed to get it working using jQuery. But can't seem to get it working with javascript.
The click event does not seem to be registering and I don't know why.
I've checked all the class names so they match in the same in both the HTML and javascript
Here is the jQuery code that works:
import $ from 'jquery';
class Search {
constructor() {
this.openButton = $('.js-search-trigger');
this.closeButton = $('.search-overlay__close');
this.searchOverlay = $(".search-overlay");
this.events();
}
events() {
this.openButton.on('click', this.openOverlay.bind(this));
this.closeButton.on('click', this.closeOverlay.bind(this));
}
openOverlay() {
this.searchOverlay.addClass("search-overlay--active");
}
closeOverlay() {
this.searchOverlay.removeClass("search-overlay--active");
}
}
export default Search;
Here is the javascript code that does not work:
class Search {
constructor() {
this.openButton = document.querySelector('.js-search-trigger');
this.closeButton = document.querySelector('.search-overlay__close');
this.searchOverlay = document.querySelector('.search-overlay');
this.events();
}
events() {
this.openButton.addEventListener('click', function() {
this.openOverlay.bind(this);
});
this.closeButton.addEventListener('click', function() {
this.closeOverlay.bind(this);
});
}
openOverlay() {
this.searchOverlay.classList.add('search-overlay--active');
}
closeOverlay() {
this.searchOverlay.classList.remove('search-overlay--active');
}
}
export default Search;
No errors were shown in the javascript where the overlay was not showing.
You'll probably want to change your event listeners to use the correct this binding:
this.openButton.addEventListener("click", this.openOverlay.bind(this));
Or use an arrow function to go with your approach - but make sure you actually call the resulting function, as in the above approach the function is passed as a reference and is called. If you removed the additional () from the code below, it would be the same as writing a function out in your code normally - it would be defined, but nothing would happen.
this.openButton.addEventListener("click", () => {
this.openOverlay.bind(this)();
});
jQuery also uses collections of elements rather than single elements, so if you have multiple elements, querySelectorAll and forEach might be in order.
If we are speaking of ecmascript-6 (I see the tag), I would recommend to use arrow function to have this inherited from the above scope, and no bind is needed:
this.openButton.addEventListener('click', () =>
this.openOverlay()
);
The problems with your code are that a) the function creates new scope with its own this; b) bound methods are not being invoked.
Why Search? You're creating an Overlay. Stick with the plan.
No need to bind anything. Use Event.currentTarget if you want to.
No need to handle .open/.close if all you need is a toggle.
And the below should work (as-is) for multiple Overlays. The overlay content is up to you.
class Overlay {
constructor() {
this.toggleButtons = document.querySelectorAll('[data-overlay]');
if (this.toggleButtons.length) this.events();
}
events() {
this.toggleButtons.forEach(el => el.addEventListener('click', this.toggleOverlay));
}
toggleOverlay(ev) {
const btn = ev.currentTarget;
const sel = btn.getAttribute('data-overlay');
const overlay = sel ? document.querySelector(sel) : btn.closest('.overlay');
overlay.classList.toggle('is-active');
}
}
new Overlay();
*{margin:0; box-sizing:border-box;} html,body {height:100%; font:14px/1.4 sans-serif;}
.overlay {
background: rgba(0,0,0,0.8);
color: #fff;
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
padding: 5vw;
transition: opacity 0.4s, visibility 0.4s;
visibility: hidden;
opacity: 0;
}
.overlay.is-active {
opacity: 1;
visibility: visible;
}
<button type="button" data-overlay="#search">OPEN #search</button>
<button type="button" data-overlay="#qa">OPEN #qa</button>
<div class="overlay" id="search">
<button type="button" data-overlay>CLOSE</button>
<h2>SEARCH</h2>
<input type="text" placeholder="Search…">
</div>
<div class="overlay" id="qa">
<button type="button" data-overlay>CLOSE</button>
<h2>Q&A</h2>
<ul><li>Lorem ipsum</li></ul>
</div>
The above is still not perfect, still misses a way to "destroy" events and not re-attach duplicate events to already initialised buttons when trying to target dynamically created ones.
Also, the use of Classes for the above task is absolutely misleading and unnecessary.