I created a navigation with hover effect with the Modernizer.mq method:
$('ul#mainNav > li').hover(function() {
if(Modernizr.mq('(min-width: 701px)'))
{
Since I want a hover effect for the navigation when width is larger then 700px and a click-mobile like navigation when the width is less then 700px.
I used the above code instead of
$('ul#mainNav > li').hover(function() {
if($( window ).width()> 701)
{
because $( window ).width() is different from my media-queries.
For IE < 8 I use respond.js to get my mediq-queries working. However, I noticed that my hover does not work anymore because the method Modernizr.mq is not working. Is there a way to find out if the method Modernizr.mq is supported?
Something like:
var mqWorks = checkIfMqWorks();
$('ul#mainNav > li').hover(function() {
if(mqWorks){
if(Modernizr.mq('(min-width: 701px)'))
{...}
} else {
if($( window ).width()> 701)
{...}
}
You can use the typeof contruct in an if then statement.
Live Example http://codepen.io/larryjoelane/pen/NxZKWr
JavaScript:
//test conditions
var modernizrLoaded = typeof Modernizr === "object";
var modernizrMQLoaded = typeof Modernizr.mq === "function";
var modernizrMinWidth = Modernizr.mq('(min-width: 10px)');
//debugging tests
console.log(modernizrLoaded);
console.log(modernizrMQLoaded);
console.log(modernizrMinWidth);
//if Modernizr is object that is loaded. It contains an mq function and the test function call for mq returns true
if (modernizrLoaded && modernizrMQLoaded && modernizrMinWidth) {
console.log("Modernizr is a function and the mq function call returns true.");
} else {
console.log("false")
}
You could then create a function to call or just place your code in the if block to run if the conditions are met and place your code for when the conditions are not met in the else block.
Related
I'm trying to apply a series of classes to a nav that cause it to shrink if the window is either resized or scrolled passed a certain point. However, I believe my conditions are cancelling each other out, and I'm not sure how to structure the if-statements or if it is better to just pass a variable.
Here's what I am trying to accomplish:
If the window is resized between a certain set of media queries, shrink the nav
If the window is outside of those break-points, and the user scrolls passed 1 (I used one just for example purposes in my code so I could see it right away) the nav will shrink and if they scroll up again, it will become it's full size
My code is here:
$(document).ready(function() {
var logo = $('.logo');
var topLevelListItems = $('.mainNav li');
var navShrink = false;
var mQ = window.matchMedia( '(min-width: 100px) and (max-width: 770px)' );
$(window).resize(function() {
if ( mQ.matches ) {
$('nav').addClass('shrink');
$('.nav-fixedWidth').addClass('shrink');
logo.addClass('shrink');
topLevelListItems.addClass('shrink');
navShrink = true;
}
else{
$('nav').removeClass('shrink');
$('.nav-fixedWidth').removeClass('shrink');
logo.removeClass('shrink');
topLevelListItems.removeClass('shrink');
navShrink = false;
}
});//end of resize function
$(window).onscroll(function(){
if( !(mQ.match) && $(this).scrollTop >= 1 ) {
$('nav').addClass('shrink');
$('.nav-fixedWidth').addClass('shrink');
logo.addClass('shrink');
topLevelListItems.addClass('shrink');
navShrink = true;
} else {
$('nav').removeClass('shrink');
$('.nav-fixedWidth').removeClass('shrink');
logo.removeClass('shrink');
topLevelListItems.removeClass('shrink');
navShrink = false;
}
});//end of scroll function
});//end of doc
Could it simply be a typo in your onscroll function?
i.e you have:
if( !(mQ.match) && $(this).scrollTop >= 1 ) { ...
should it not be:
if( !(mQ.matches) && $(this).scrollTop >= 1 ) { ...
what I'm trying to do
I have dropdown-menus that open on hover and the parent menus have their own landing page link. we're not willing to sacrifice this behavior, but if obviously creates problem for large touch enabled devices. So, I'm detecting touch devices with jquery, and disabling the parent menu click on devices larger than 990px wide. devices below 990px is considered as mobile view and it switches to toggle. This switch between the toggle and the desktop view is expected to continue on screen rotation too.
what is happening
the menu link is disabled on first load and works as expected. Then I rotate the screen (from landscape to portrait) and see the mobile menu as expected and navigate to another page. once the page loads, I rotate it again (from portrait to landscape) and the desktop view is visible, but the parent links are clickable now!
I want to prevent this click event on second rotation too. HTML is standard bootstrap 3 navigation code and my js is like this:
function isTouchDevice() {
return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}
$(document).ready(function () {
$(window).resize(function () {
var o = $(window).innerWidth();
function isTouchDevice() {
return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}
if ((isTouchDevice() === true) && (o >= 990)) {
$('.navbar .dropdown > a ').each(function () {
$(this).on("click", function(){
return false
})
})
alert('oi!!')
}
else {
$('.navbar .dropdown > a ').each(function () {
$(this).on("click", function(){
location.href = this.href;
})
});
alert ("bad!") //for debugging purpose, not really needed
}
}).resize();
//the mobile menu clicks events
$('#menu .dropdown > a ').click(function () {
location.href = this.href;
});
});
PS this is a website, not an android app. I have found answers that answer this type of questions for android apps.
Update the jsfiddle for my code
I solved it myself. Turns out, the condition for width checking was creating the problem and in my case, unnecessary, because bootstrap is already hiding the menu in smaller screens and I was targeting touch enabled desktop devices anyway. so I took off && (o >= 990) from the if condition and it is working as expected.
full js is below (in case anyone needs it). I used the timer to prevent the event from firing before the resize, but it will probably work without the timer too. :
$(document).ready(function () {
var resizeTimer;
$(window).on('resize', function(e){
clearTimeout(resizeTimer);
resizeTimer = setTimeout(function () {
function isTouchDevice() {
return true == ("ontouchstart" in window || window.DocumentTouch && document instanceof DocumentTouch);
}
if (isTouchDevice() === true) {
$('.navbar .dropdown > a ').click(function () {
return false
});
console.log("landscape")
}
else {
$('.navbar .dropdown > a ').each(function () {
$(this).on("click", function(){
location.href = this.href;
})
});
console.log("portrait")
}
}, 250)
}).trigger('resize');
});
I think this is a problem with the way you are recognizing the mobile device. For checking device sizes I would not suggest using $(window).innerWidth(). What you are doing now does not check the screen size, rather it checks the window size, which fluctuates when switching orientation.
I would like to suggest that instead of checking for only >900px, that you check for the entire area of the device (width x height) so landscape and portrait would act the same way. And I would like to suggest using screen.availHeight * screen.availWidth to determine this.
I really hope this helps you with your problem. Please let me know if not and I'll help you debug.
What I would like to do is create window breakpoints, like 300, 400, 500
and perform a certain action when the window reaches a certain breakpoint, ideally could be something like:
300: function(){...}
400: function(){...}
500: function(){...}
and the following code is how I tried to fire only 1 time a certain function associated to a certain breakpoint, but actually I'm stuck because I don't know how to pass them dynamically inside a single little function, and because all breakpoints in my code look pretty the same and they are useless repeated...
breakpoints = [300, 400, 500];
function screenBreakpoints(){
var regex = / ?breakpoint[0-9] ?/;
if( $(window).width() < breakpoints[0] ){
if( !$('html').hasClass('breakpoint0') ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass('breakpoint0');
console.log('breakpoint0');
$('li.MOVING').insertBefore('ul li.one'); // breakpoint function (fires only one time)
}
}
else if( $(window).width() > breakpoints[0] && $(window).width() < breakpoints[1] ){
if( !$('html').hasClass('breakpoint1') ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass('breakpoint1');
console.log('breakpoint1');
$('li.MOVING').insertBefore('ul li.two'); // breakpoint function (fires only one time)
}
}
else if( $(window).width() > breakpoints[1] && $(window).width() < breakpoints[2] ){
if( !$('html').hasClass('breakpoint2') ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass('breakpoint2');
console.log('breakpoint2');
$('li.MOVING').insertBefore('ul li.three'); // breakpoint function (fires only one time)
}
}
else if( $(window).width() > breakpoints[1] ){
if( !$('html').hasClass('breakpoint3') ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass('breakpoint3');
console.log('breakpoint3');
$('li.MOVING').insertBefore('ul li.four'); // breakpoint function (fires only one time)
}
}
}
$(window).resize(function(){
screenBreakpoints();
});
FIDDLE
First you get the window width using jquery's width() function.
Then declare three variables accordingly for the three sizes and write your functions inside that variable according to the condition
Keep the other two variables empty based on the width size and call the screenBreakpoints
NOTE: As #flowtron said in the comments, you can use <= or >= in the if condition based on your requirements
$(window).resize(function(){
var viewportWidth = $(window).width();
if(viewportWidth == 300){ // Use <= or >= based on ur requirements
var function300= function300(){};
var function400 ='';
var function500='';
screenBreakpoints(function300,function400,function500);
}
else if(viewportWidth == 400){ // Use <= or >= based on ur requirements
var function400= function400(){};
var function300 ='';
var function500='';
screenBreakpoints(function300,function400,function500);
}
else if(viewportWidth == 500){ // Use <= or >= based on ur requirements
var function500= function500(){
var function300 ='';
var function400='';
};
screenBreakpoints(function300,function400,function500);
}
});
Then in your screenBreakpoints function check which parameter is empty
For Ex:
function screenBreakpoints(function300,function400,function500){
if( $(window).width() < breakpoints[0] ){
if(function300 != null && function300 != undefined){
if( !$('html').hasClass('breakpoint0') ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass('breakpoint0');
console.log('breakpoint0');
$('li.MOVING').insertBefore('ul li.one'); // breakpoint function (fires only one time)
}
}
}
}
In my work I've come to use a set of DIVs (usually invisible, not via display: none though!) that have an ID of "ThisIsMediaQuery_Alpha", .. Beta, Gamma .. however you name them and as many as you need - then each breakpoint you're interested in just has to modify one of these from the default settings all got initially - I usually use a background-color (legacy from when they were created to FIX a MediaQuery-usage and were visible), but anything you like can work really. Then just call whichMediaQueryIsThis() on $(window).resize(), which iterates over the DIVs and finds out which one has the "marker value" (e.g. cur.style.backgroundColor=="red"). Then just use a case to switch out the appropriate values when calling your manageMediaQuerySwitch(arg0,arg1,arg2) function. HTH
The upside of this is, it also works with MediaQueries that aren't pixel-oriented! ;-)
Here is a complete edit that is done to the js file from the jsfiddle
the you provided. Just copy paste this over there or, just run this jsfiddle. I found it working. What I'm doing is, I'm just making a single function and calling it with different parameters.
Here is your modified js
breakpoints = [300, 400, 500];
function screenBreakpoints(breakpoint,listName){
var regex = / ?breakpoint[0-9] ?/;
if( !$('html').hasClass(breakpoint) ){
$('html')[0].className = $('html')[0].className.replace(regex, '');
$('html').addClass(breakpoint);
console.log(breakpoint);
$('li.MOVING').insertBefore(listName); // breakpoint function (fires only one time)
}
}
$(window).resize(function(){
if( $(window).width() < breakpoints[0] ){
screenBreakpoints("breakpoint0","ul li.one");
}
else if( $(window).width() < breakpoints[1] ){
screenBreakpoints("breakpoint1","ul li.two");
}
else if( $(window).width() < breakpoints[2] ){
screenBreakpoints("breakpoint2","ul li.three");
}
else{
screenBreakpoints("breakpoint3","ul li.four");
}
});
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 have a simple problem, but I can't find the solution ...
I just want to launch an event (which execute a method) when I scroll my page up and I "touch" the top of it. I'm using JavaScript and jQuery in my page. Thanks in advance !
You should use the scroll event for that purpose:
$(window).scroll(function() {
if ($(this).scrollTop() == 0) {
//Do whatever you want to do
}
});
One thing you should notice is that this way when somebody scrolls to top continuously the even will be fired although you only want it once the top is hit, for this you can define the event handler as a function and put the last scrolltop value into a function local variable.
$(window).scroll(handleHitTop);
function handleHitTop(event) {
var currentScrollTopValue = $(this).scrollTop();
if (handleHitTop.lastTop === undefined) {
handleHitTop.lastTop = currentScrollTopValue ;
return;
}
if (handleHitTop.lastTop == 0 && currentScrollTopValue == 0) {
return;
}
handleHitTop.lastTop = currentScrollTopValue;
if (handleHitTop.lastTop == 0) {
//Call your event here
}
}
Use the onscroll event. Vanilla js example:
window.onscroll = function() {
if(document.body.scrollTop == 0) {
alert('yay!');
}
}
http://jsfiddle.net/kuWuf/
Use a .scroll() handler and .scrollTop():
$(window).scroll( function() {
if($(this).scrollTop() == 0) {
alert("Top!!!");
}
});
http://jsfiddle.net/jtbowden/wvJ9r/