I'm curious is there an event listener or perhaps a way to construct a method that will trigger when a CSS change happens?
My stylesheet uses media queries and I want to know if there's a way to attach a listener to see when those media queries kick in and out. For example I have a media query that hides a button at certain screen widths
#media screen and (max-width: 480px) {
#search-button {
display: none;
}
}
What event listener would I use to detect when that display changes? I'm currently doing this:
$(window).resize(function() {
if($('#search-button').css("display") == "none") {
//do something
} else {
//do something else
}
});
Which works fine, but it calls the listener every time the user changes the screen and I'd rather just have it fire only when the css of the button changes. I hope that makes sense.
for example this is what I'd like
$('#search-button').cssEventListenerOfSomeKind(function() {
alert('the display changed');
});
Binding to the window.resize is your best option (I believe). There isn't any event fired when you change an element's CSS. You can however optimize a bit by caching the selector used:
var $searcButton = $('#search-button');
$(window).resize(function() {
if($searcButton.css("display") == "none") {
//do something
} else {
//do something else
}
});
Or you can use $(window).width() to check the width of the viewport:
var $window = $(window);
$window.resize(function() {
if($window.width() <= 480) {
//do something
} else {
//do something else
}
});
UPDATE
You can always throttle your own event handler:
var $window = $(window),
resize_ok = true,
timer;
timer = setInterval(function () {
resize_ok = true;
}, 250);
$window.resize(function() {
if (resize_ok === true) {
resize_ok = false;
if($window.width() <= 480) {
//do something
} else {
//do something else
}
}
});
This will prevent the code in your resize event handler from running more than once every quarter second.
If it is only a one time event you could try to unbind the event.
http://api.jquery.com/unbind/
I know this is old but I managed to solve it with this logic
// set width and height of element that is controlled by the media query
var page_width = $page.width();
var page_height = $page.height();
$window = $(window).resize(function(){
if( $page.width() != page_width ) {
// update page_width and height so it only executes your
// function when a change occurs
page_width = $page.width();
page_height = $page.height();
// do something
// ...
}
});
Related
I am trying to execute a window resize and page reload event simultaneously. When the screen size is less than 768 px, I am adding an attribute to an element. I also need that attribute added when the page is reload and a specific size as well, not just when its resized. The code I have below works, except when my screen size hs < 769 px, it takes a few seconds for the attribute to be added which affects how it looks. Any tips on how I can fix this?
window.onload = function(event) {
var element = document.querySelector('.filter-select');
if (window.innerWidth < 768) {
element.classList.add('testing');
element.removeAttribute("size", "4")
element.removeAttribute("multiple", "yes")
} else {
element.classList.remove('testing');
}
}
document.addEventListener("DOMContentLoaded", function(event) {
var element = document.querySelector('.filter-select');
function resize() {
if (window.innerWidth < 768) {
element.classList.add('testing');
element.removeAttribute("size", "4")
element.removeAttribute("multiple", "yes")
} else {
element.classList.remove('testing');
}
}
window.onresize = resize;
});
My only guess is that you doubled up the same process under different events and these events happen at different times thus the NOTICABLE lag.. if this doesn't solve.. this is an amazing question I already upvoted..
function resize() {
var element = document.querySelector('.filter-select')
if (window.innerWidth < 768) {
element.classList.add('testing')
element.removeAttribute("size", "4")
element.removeAttribute("multiple", "yes")
}
else {
element.classList.remove('testing');
}
}
window.onload=resize
I want the following:
Detect page width on load and add/remove class if it's below/above 959px
If I resize the page I want to do the same
$(window).on("resize load", function(e) {
e = $("body").width();
if (e <= 959) {
$("#button").addClass("active")
}
if (e >= 960) {
$("#button").removeClass("active")
}
})
This code works, but it removes the active class even if I resize the window from 500px to 501px. I want that to only add the class if I go above 960px or remove it if I go below 959px. How can I do that?
EDIT
Thanks for the answers! In the meantime I figured out a solution that works and suit my needs.
$(window).one("load", function () {
r = $("body").width();
if (r >= 960) {
$("body").attr("mobile","0")
//do something
}
if (r <= 959) {
$("body").attr("mobile","1")
//do something
}
});
$(window).on("resize", function() {
r = $("body").width();
if ($("body").attr("mobile") == "0") {
if (r <= 959) {
//do something
$("body").attr("mobile","1")
}
}
if ($("body").attr("mobile") == "1") {
if (r >= 960) {
//do something
$("body").attr("mobile","0")
}
}
})
Explanation:
It's a very specific solution since I modify the tabindex values in mobile view and I don't want to change these values back to 0 on a simple resize, only in the case I switch from mobile view to desktop.
The width of the window is different than the width of the body. Using $('body').width() will account for the overflow, whereas using $(window).width() will give you the actual screen width.
$(window).on('load resize', function() {
$('#button').toggleClass('active', $(this).width() <= 959)
});
However, using media queries is much more straight forward if in fact, you are just adding CSS properties.
#button {
opacity: 0.5;
}
#media (max-width: 959px) {
#button {
opacity: 1;
}
}
You could ouse window.matchMedia for this. If you look at the perf test, matchMedia is a lot faster than resize.
var mq = window.matchMedia("(min-width:959px)");
// onload
setButton(mql);
// add listener for the query
mq.addListener(setButton);
function setButton(mq) {
if (mql.matches) {
// do something with your setButton
} else {
// no match....
}
}
Here you go with a solution https://jsfiddle.net/hLkv1xan/1/
$(window).on("resize load", function(e) {
e = $("body").width();
if (e <= 959) {
$("#button").addClass("active")
} else {
$("#button").removeClass("active")
}
});
.active{
background: red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button id="button">
Submit
</button>
I just modified your code a bit, change in the condition.
Hope this will help you.
I'm creating a responsive template and I want to remove the listeners on an element when screen is being resized or is smaller than the specified width.
Imagine an menu which when you hover on it's items, it shows you the sub-menus in normal displays but the same menu in mobile devices will show the sub-menus only by tapping or clicking on the items.
I can't make the undelegate work. In resized screen I still have the mouseover and mouseout event-listeners. I'm not getting any errors in console and I've tried both:
.off('mouseover', 'li')
.off('mouseover')
.undelegate('li', 'mouseover')
.undelegate('li')
and none of them works.
var $window = $(window);
function handleSidenav() {
$(".nav-list").delegate('li', 'mouseover', function(e) {
$(this).find("a").addClass('active');
$(this).find("div.sub-items").toggle();
}).delegate('li', 'mouseout', function(e) {
$(this).find('a').removeClass('active');
$(this).find("div.sub-items").toggle();
});
}
function checkWidth() {
var windowsize = $window.width();
if (windowsize < 767) {
smallScreenDelegation();
} else {
SmallScreenUndelegation();
}
}
checkWidth();
handleSidenav();
$window.resize(checkWidth());
function smallScreenDelegation() {
$(".nav-list").undelegate('li'); //It's not working
$(".nav-list").undelegate('li'); //It's not working
$(".nav-list").delegate('li a:first', 'click', function(event) {
if ($(this).next().is(':hidden')) {
$(this).addClass('active');
$(this).next().slideDown('slow');
} else {
$(this).removeClass('active').next().slideUp('slow');
}
event.preventDefault();
});
}
You need to wrap window in the jQuery object. I'm not sure if you set $window = $(window), but it seems here that $window.width() and $window.resize(checkWidth) are missing parenthesis. I was able to get it working fine once I changed those to $(window). You have to define which event you want to undelegate. I used:
$('.nav-list').undelegate('li', 'mouseover');
Open up console and you can see that it works: http://jsbin.com/efonut/6/edit
Also, it's really best to use .on() and off() vs .delegate() and .undelegate(), but at least this works...
I still don't know what was wrong with undelegate which I couldn't make it work, but I managed to fix my code by using on and off.
As adeneo said I was delegating and undelegating on each window resize which was quiet a bug and I think I fixed that but holding the last state on device variable.
var $window = $(window);
var device;
function desktopSidenav() {
$(".nav-list > li").off('click');
$(".nav-list > li").on('mouseover', function(e) {
$(this).find("a").addClass('active');
$(this).find("div.sub-items").toggle();
}).on('mouseout', function(e) {
$(this).find('a').removeClass('active');
$(this).find("div.sub-items").toggle();
});
}
function handheldSidenav() {
$(".nav-list > li").off('mouseover').off('mouseout');
$(".nav-list > li").on('click', function(e) {
if ($(this).find("div.sub-items").is(':hidden')) {
$(this).find("a:first").addClass('active').next().slideDown('slow');
} else {
$(this).find("a:first").removeClass('active').next().slideUp('slow');
}
e.preventDefault();
});
}
Now I check the window size before doing anything else an I'll hold the device type in device variable. If window is resized, I'm gonna check the device state and do the things based on device type.
if ($window.width() > 767) {
device = 'desktop';
desktopSidenav();
} else {
device = 'handheld';
handheldSidenav();
}
$window.resize(function() {
if ($window.width() > 767) {
if (device == 'handheld') {
device = 'desktop';
desktopSidenav();
}
} else {
if (device == 'desktop') {
device = 'handheld';
handheldSidenav();
}
}
});
If I use delegate and undelegate instead of on and off, the code won't work and I still don't know why, so this cannot be count as a real answer, but I wanted to tell everyone who has a similar problem to use jQuery's on and off instead on delegate.
I've got a fairly simple navigation menu that opens and closes on click. The menu behaviour only comes into play when the browser viewport is below a certain size.
It all works great 90% of the time. The remaining 10% of the time (when I'm demonstrating it to the client, natch) the click event doesn't fire at all. As far as I can tell, the problem only occurs after the browser has been resized a few times, but as it usually works normally when the window has been resized, it's difficult to track down why it's happening.
Code:
var smallViewport = false;
$(document).ready(function(){
if($(window).width() < 520) {
smallViewport = true;
}
if(smallViewport == true) {
$('nav.main').click(function(){
console.log(' + clicky clicky');
if($(this).find('.level-1').hasClass('open') == true) {
$(this).find('.level-1').slideUp('fast').removeClass('open');
} else {
$(this).find('.level-1').slideDown('fast', function(){ $(this).addClass('open'); });
}
})
}
});
$(window).resize(function() {
if($(window).width() < 520) {
smallViewport = true;
} else {
smallViewport = false;
}
console.log(smallViewport);
if(smallViewport == true) {
$('.level-1').removeClass('open').css('display','none');
} else {
$('.level-1').css('display','block');
}
});
When the problem chooses to manifest itself, console.log(smallViewport) in the resize function outputs 'true' when it should be true, the click event just refuses to fire along with it.
Has anybody encountered a similar problem before? Any obvious solutions I'm missing?
You're only binding the click when the page loads, not when it's resized
if $(window).width() < 520 evaluates as false on the page load, the click event will not be bound - which is why your console log is correct but the event is not firing
Put the viewport check inside the click event handler. As it is now, the event handler isn't bound if the check evaluates to false on page load. Try changing it to this:
$('nav.main').click(function(){
if(smallViewport == true) {
console.log(' + clicky clicky');
if($(this).find('.level-1').hasClass('open') == true) {
$(this).find('.level-1').slideUp('fast').removeClass('open');
} else {
$(this).find('.level-1').slideDown('fast', function() {
$(this).addClass('open');
});
}
}
});
Any insights on how to catch a scrolling event on a element that has overflow:hidden? I would like to scroll in a column without showing a scrollbar to the user.
This is actually a somewhat indepth process. What I do is set global flags when users mouse enters and leaves the element that you want to scroll. Then, on the mousewheel event for the body I check to see if the MOUSE_OVER flag is true, then stop propagation of the event. This is so the main body doesnt scroll in case your entire page has overflow.
Note that with overflow hidden, the default scrolling ability is lost so you must create it yourself. To do this you can set a mousewheel listener on your div in question and use the event.wheelDelta property to check whether the user is scrolling up or down. This value is different according to browser, but it is generally negative if scrolling down and positive if scrolling up. You can then change position of your div accordingly.
This code is hacked up quickly but it would essentially look like this...
var MOUSE_OVER = false;
$('body').bind('mousewheel', function(e){
if(MOUSE_OVER){
if(e.preventDefault) { e.preventDefault(); }
e.returnValue = false;
return false;
}
});
$('#myDiv').mouseenter(function(){ MOUSE_OVER=true; });
$('#myDiv').mouseleave(function(){ MOUSE_OVER=false; });
$('#myDiv').bind('mousewheel', function(e){
var delta = e.wheelDelta;
if(delta > 0){
//go up
}
else{
//go down
}
});
I use overflow:scroll, but also Absolutely position a div over the scroll bar in order to hide it.
$("body").css("overflow", "hidden")
$(document).bind('mousewheel', function(evt) {
var delta = evt.originalEvent.wheelDelta
console.log(delta)
})
works for me. adapted from How do I get the wheelDelta property?
I edited #anson s answer to Vanilla Javascript since it may be useful for others. Also note that "mousewheel" event is deprecated. So my code uses "wheel" instead. Next to that I added arrow functions for practical access the to "this".
fixScrollBehavior(elem) {
elem.addEventListener('scroll', (e) => {
console.log('scrolling');
});
let MOUSE_OVER = false;
elem.addEventListener('wheel', (e) => {
if (MOUSE_OVER) {
if (e.preventDefault) {
e.preventDefault();
}
e.returnValue = false;
return false;
}
});
elem.addEventListener('mouseenter', () => {
MOUSE_OVER = true;
});
elem.addEventListener('mouseleave', () => {
MOUSE_OVER = false;
});
elem.addEventListener('wheel', (e) => {
let delta = e.wheelDelta;
if (delta > 0) {
//go up
} else {
//go down
}
});
}
Note that this does not fix the mobile touch-"scroll"s.
$("div").on('wheel', function (e) {
if (e.originalEvent.deltaY < 0) {
console.log("Scroll up");
} else {
console.log("Scroll down");
}
});
This did the trick for me.
JSFiddle
StackFiddle:
$("div").on('wheel', function(e) {
if (e.originalEvent.deltaY < 0) {
console.log("Scroll up");
} else {
console.log("Scroll down");
}
});
div {
height: 50px;
width: 300px;
background-color: black;
overflow: hidden;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div></div>
I am late, but I think I have a better answer.
Style your container as overflow: overlay, this will free up space of scrollbar, then style scrollbar or hide it or make its handle height/width 0,
Then you should get scroll events also.
Note : styling the scrollbar is not supported in all web browsers.