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.
Related
So basically I'm developing a website where the scroll event's default is prevented and instead each scroll takes you down or up to the next "section". This works really well on PC and some phones but on iOS the scrolling just becomes this jumbled up mess and quite frankly I'm out of ideas.
The scroll API for mobile I'm using is jquery touchSwipe
https://github.com/mattbryson/TouchSwipe-Jquery-Plugin
And the following code is the function that is supposed to do all the work.
if( /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent) ) {
$(window).swipe({
swipe:function(event, direction, distance, duration, fingerCount, fingerData) {
if (scrolling == false && direction === "down") {
scrolling = true;
scrollSection = Math.round($(window).scrollTop()/window.innerHeight);
scrollTo_(event,sections[scrollSection-1]);
setTimeout(function() {
scrolling = false;
}, 1000);
}
else if (scrolling == false && direction === "up") {
scrolling = true;
scrollSection = Math.round($(window).scrollTop()/window.innerHeight);
scrollTo_(event,sections[scrollSection+1]);
setTimeout(function() {
scrolling = false;
}, 1000);
}
}
});
}
If you have any questions by all means let me know and I will update this question.
Have you tried with this?
In my web app last days I've worked on Jquery UI slider, that's didn't worked for mobiles, so after attaching that it get's worked.
I'm trying to build a Safari Extension where when a user hits Command+B it will show the popover. Using the code below it works but always shows the popover on a different window not the current window/tab. I would like it to display the popover on the current window instead of switching to a different window and opening the popover there. It works perfectly if there is only one Safari window open but starts to have problems when multiple windows are open.
Any ideas?
Global Page File:
<script>
safari.application.addEventListener('message', function (e) {
if (e.name == 'Show Popover') {
safari.extension.toolbarItems[0].showPopover();
}
}, false);
</script>
Injected Content:
document.addEventListener("keydown", keydown);
function keydown(event) {
if ( event.metaKey && event.keyCode == 66) {
event.preventDefault();
safari.self.tab.dispatchMessage('Show Popover', {});
}
}
This is because you are manually selecting the first toolbarItem here;
safari.extension.toolbarItems[0].showPopover();
You need to determine which toolbarItem the popover needs to appear on;
Something like this;
var toolBarID = 'my_id';
var activeItem = safari.extension.toolbarItems.filter(function (button) {
return button.identifier == toolBarID && button.browserWindow == safari.application.activeBrowserWindow;
})[0];
You then use this object for the showPopover function;
activeItem.showPopover();
Hope this helps
I have the following code which opens a div element on a touch screen device when the main nav menu link (.nav-landing > li) has been tapped. If the nav menu link is tapped for the second time the link becomes active and loads the page link. This all works fine but I can't figure out how to close the div element (.touch-device) if the user stays on the page and no longer needs to see the content within the div element.
(function ($) {
var landingLink = '.nav-landing > li';
// BROWSER WITH EITHER TOUCH EVENTS OR POINTER EVENTS RUNNING ON TOUCH-CAPABLE DEVICE
if (('ontouchstart' in window) || (navigator.maxTouchPoints > 0) || (navigator.msMaxTouchPoints > 0)) {
// DISPLAY LANDING PAGE MEGA NAV ON TOUCH
$(landingLink).live('touchstart', function (e) {
'use strict';
var link = $(this);
var megaDiv = $(this).find('div');
// REMOVE touch-device CLASS IF NAV LINK HASN'T BEEN TAPPED
if (!megaDiv.hasClass('touch-device')) {
$('.touch-device').removeClass('touch-device');
}
if (link.hasClass('hover')) {
// SECOND TOUCH
return true;
} else {
// FIRST TOUCH
link.addClass('hover');
megaDiv.addClass('touch-device');
$(landingLink).not(this).removeClass('hover');
e.preventDefault();
return false;
}
});
$('.close-div').click(function () {
//alert('clicked');
});
} else { // STANDARD DESKTOP BROWSERS WITH MOUSE HOVER
// DISPLAY LANDING PAGE MEGA NAV ON HOVER
$(landingLink).has('div').hover(function () {
$(this).addClass('hover').find('div').show();
}, function () {
$(this).removeClass('hover').find('div').hide();
});
}
})(jQuery);
I've also setup a JSFiddle to show the full example:
JSFiddle
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');
});
}
}
});