jQuery click function slow (lagging) on mobile - javascript

So i have this click function that toggles a dropdown-menu, but on Smartphones this is slow and not smooth.
Is there any other way to make this dropdown work with a smooth transition on any mobile device using jQuery?
(I've heard about "vclick" but couldn't find out how to make this work.
$j('.dropdown-menu').click(function() {
$j('.dropdown-menu').not(this).children('ul').slideUp("slow");
$j(this).children('ul').slideDown("slow");
});
$j('.dropdown-menu').blur(function() {
$j('.dropdown-inside').hide('slow', function() {
});
});

Try the click on Id rather then class. Class is always slow. So suppose if your dropdown has id like 'myDropdown' then do it like
$j('#myDropdown').click(function(){
//your code here
});

The first part can be simplified to this:
$j('.dropdown-menu').click(function() {
$j('.dropdown-menu').not(this).children('ul').slideToggle(2000);
});
Also, try wrapping the hidden content in a div and giving it a width. When you click on the div, it actually pulls it out of position to measure it before quickly replacing it because JQuery doesn't know the dimensions of your hidden div until it's displayed. So . This could make the whole animation laggy!
Another suggestion:
ID is faster than class. Part of the reason is that ID is supposed to be unique, so the API stops searching after the ID is found in the DOM.
If you must use a class or attribute selector, you can improve performance by specifying the optional context parameter.
Credits

Cause
According to Google :
...mobile browsers will wait approximately 300ms from the time that you tap the button to fire the click event. The reason for this is that the browser is waiting to see if you are actually performing a double tap.
Solution
1. Use fastclick.js to get rid of this 300ms lag
https://github.com/ftlabs/fastclick
2. Use application cache to speed up the load
https://developer.mozilla.org/en-US/docs/Web/HTML/Using_the_application_cache

Related

Disable Double Tap To Click On Touchscreen iOS Devices

So, quite recently I've been working on a website that was given me to improve and make responsive, and one of the issues that I've been faced with is that there are many elements that are clickable, with a mixture of CSS and jQuery effects for hover states.
Now, firstly I'd prefer all of these hover states to be CSS, but the main issue I'm having is that on these hover states, certain elements are changing display and visibility css properties. I did some reading, and apparently if this is the case, on touchscreen iOS devices, this causes the first 'touch' to force the hover state, and then a second click is needed to actually click the element.
I'm trying to find a solution that doesn't require lots of markup and styling changes. Preferably a fix harnessing jQuery/JavaScript would be good.
I've tried the following:
$(document).ready(function() {
$('a').on('click touchend', function(e) {
var el = $(this);
var link = el.attr('href');
window.location = link;
});
});
However, this has issues when the user holds their finger down on a clickable element, and drags the page to scroll. When they release their finger after dragging, the window.location is still changed.
I'll add a jsFiddle later if necessary.
Thanks in advance.
EDIT:
Here's a jsFiddle that shows the issue. http://jsfiddle.net/0bj3uxap/4/
If you tap one of the blocks, you'll see it shows the hover state, you then need to tap again to actually fire the click event.
It seems to happen when an element is hidden, and then the hover state shows the element.
Looks like I found a solution.
https://github.com/ftlabs/fastclick
FastClick fixes this issue, and removes the 300ms delay issue with some mobile browsers.
Just include the library in <script> tags, and then initiate it using jQuery, or whatever you prefer:
$(document).ready(function() {
FastClick.attach(document.body);
});
Just to explain briefly why the issue occurs:
When an element is hidden, for example when it has a CSS property of any of the following:
display: none;
opacity: 0;
visibility: hidden;
And the hover state of the hidden element then shows the element, iOS doesn't fire a click event on the first touch, it forces the hover state (to show the element). The user then needs to touch the element again for a click event to fire.
I see why this has been added, but I think I'd rather iOS didn't do this, and then developers would just need to tailor their websites to not hide content that coud be vital.
If it helps anyone else: In my case I had a very similar problem, however it wasn't simply due to a :hover style on it's own. Instead, it was due to the fact that I was using JavaScript event listeners (touchstart, touchmove and touchend) to change visibility of elements on the page (no matter where).
In my case, I was simply adding a touch class to the <html> tag in order to detect that the device was capable of touch and should always display certain elements that typically only show on hover. My fix was two fold:
Move to a >300ms delay (i.e. the amount of time mobile browsers may typically wait before determining if this was a single vs. double click). In my case, I just settled on 500ms (see #2 below for why).
I then used a cookie to temporarily retain this setting so that these elements would be visible immediately and no touch event listeners would be required on subsequent page loads (thus a delay of 500ms on the first occasion shouldn't be a deal breaker).
Example code:
In this case, using jQuery + https://github.com/carhartl/jquery-cookie (modified to support maxAge).
function initTouchSupport() {
// See if touch was already detected and, if so, immediately toggle the touch classes.
if ($.cookie('touch-device')) {
toggleTouch();
return;
}
// Be efficient and listen once and, if ever detected, flag capability and stop listening (for efficiency).
var events = 'touchstart touchmove touchend';
$body.on(events, detectTouch);
function detectTouch() {
// Detected; retain for a short while (e.g. in case this is a laptop with touch capability and they switch
// to mouse control). That way there's no delay on the next several page loads and no chance of a double-touch bug.
$body.off(events, detectTouch);
$.cookie('touch-device', true, {
path: '/',
domain: getDomain(),
maxAge: 86400 // 86400 seconds = 1 day
});
setTimeout(toggleTouch, 500);
}
function toggleTouch() {
// Swap out classes now
$html.toggleClass('no-touch', false);
$html.toggleClass('touch', true);
}
}
I had a very similar issue in IOS having to double tab buttons etc I removed all the desktop styles which included some hover styles this made no difference. I put the hover styles back in which are not used in the mobile UI. In then end the issue was a css class called
.error-message
Correction it turns out this css has been used in our UI and it was linked to a mouseover event

How to unbind() .hover() but not .click()?

I'm creating a site using Bootstrap 3, and also using a script that makes the dropdown-menu appear on hover using the .hover() function. I'm trying to prevent this on small devices by using enquire.js. I'm trying to unbind the .hover() event on the element using this code:
$('.dropdown').unbind('mouseenter mouseleave');
This unbinds the .hover of that script but apparently it also removes the .click() event(or whatever bootstrap uses), and now when I hover or click on the element, nothing happens.
So I just want to how I can remove the .hover() on that element, that is originating from that script, but not change anything else.
Would really appreciate any help.
Thanks!
Edit: Here is how I'm calling the handlers for the hover functions:
$('.dropdown').hover(handlerIn, handlerOut);
function handlerIn(){
// mouseenter code
}
function hideMenu() {
// mouseleave code
}
I'm trying to unbind them with this code.
$('.dropdown').unbind('mouseenter', showMenu);
$('.dropdown').unbind('mouseleave', hideMenu);
But its not working.
Please help!
**Edit2: ** Based on the answer of Tieson T.:
function dropdownOnHover(){
if (window.matchMedia("(min-width: 800px)").matches) {
/* the view port is at least 800 pixels wide */
$('.dropdown').hover(handlerIn, handlerOut);
function handlerIn(){
// mouseenter code
}
function hideMenu() {
// mouseleave code
}
}
}
$(window).load(function() {
dropdownOnHover();
});
$(window).resize(function() {
dropdownOnHover();
});
The code that Tieson T. provided worked the best; however, when I resize the window, until I reach the breakpoint from any direction, the effect doesn't change. That is, if the window is loaded above 800px, the hover effect will be there, but if I make the window smaller it still remains. I tried to invoke the functions with window.load and window.resize but it is still the same.
Edit 3: I'm actually trying to create Bootstrap dropdown on hover instead of click. Here is the updated jsFiddle: http://jsfiddle.net/CR2Lw/2/
Please note: In the jsFiddle example, I could use css :hover property and set the dropdow-menu to display:block. But because the way I need to style the dropdown, there needs to be some space between the link and the dropdown (it is a must), and so I have to find a javascript solution. or a very tricky css solution, in which the there is abot 50px space between the link and the dropdown, when when the user has hovered over the link and the dropdown has appeared, the dropdown shouldn't disappear when the user tries to reach it. Hope it makes sense and thanks.
Edit 4 - First possible solution: http://jsfiddle.net/g9JJk/6/
Might be easier to selectively apply the hover, rather than try to remove it later. You can use window.matchMedia and only apply your script if the browser has a screen size that implies a desktop browser (or a largish tablet):
if (window.matchMedia("(min-width: 800px)").matches) {
/* the view port is at least 800 pixels wide */
$('.dropdown').on({
mouseenter: function () {
//stuff to do on mouse enter
},
mouseleave: function () {
//stuff to do on mouse leave
}
});
}
else{
$('.dropdown').off('mouseenter, mouseleave');
}
Since it's not 100% supported, you'd want to add a polyfill for those browsers without native support: https://github.com/paulirish/matchMedia.js/
If you're using Moderizr, that polyfill is included in that library already, so you're good-to-go.
I still don't understand how you intend to "dismiss" the dropdown-menu once it is displayed upon mousing over the dropdown element partly because there's not enough code in your question, but that's sort of irrelevant to this answer.
I think a much easier way to approach the mousenter event handling portion is not by using off()/on() to unbind/bind events at a specific breakpoints, but rather to do just do a simple check when the event is triggered. In other words, something like this:
$('.dropdown').on('mouseenter', function() {
if($('.navbar-toggle').css('display') == 'none') {
$(this).children('.dropdown-menu').show();
};
});
$('.dropdown-menu').on('click', function() {
$(this).hide();
});
Here's a working fiddle: http://jsfiddle.net/jme11/g9JJk/
Basically, in the mouseenter event I'm checking if the menu toggle is displayed, but you can check window.width() at that point instead if you prefer. In my mind, the toggle element's display value is easier to follow and it also ensures that if you change your media query breakpoints for the "collapsed" menu, the code will remain in sync without having to update the hardcoded values (e.g. 768px).
The on click to dismiss the menu doesn't need a check, as it has no detrimental effects that I can see when triggered on the "collapsed" menu dropdown.
I still don't like this from a UX perspective. I would much rather have to click to open a menu than click to close a menu that's being opened on a hover event, but maybe you have some magic plan for some other way of triggering the hide method. Maybe you are planning to register a mousemove event that checks if the mouse is anywhere within the bounds of the .dropdown + 50px + .dropdown-menu or something like that... I would really like to know how you intend to do this (curiosity is sort of killing me). Maybe you can update your code to show the final result.
EDIT: Thanks for posting your solution!

Make one element disappear when another element disappears

Context: I am making a small jQuery library for modals (in-window popups): https://github.com/hypesystem/d_modal.js
When creating a new modal, it is possible to also fade the page. This is done by adding a div with a semi-transparent black background.
When the modal is removed I want the "fade" to disappear as well. But not just when the modal is .remove()'d - I want the fade to disappear in the same way as the modal on any action that makes the modal disappear: fadeOut(), hide(), etc.
Here is a jsFiddle to test in (if you have any ideas): http://jsfiddle.net/n5fqS/
What I'm looking for is one solution that handles all the cases.
there are many ways of hidding elements (removing content of div, changing css "display" property, fadeOut(), hide(), etc, etc) and Jquery does not have a universal event listener that would group all these events. I think you will have to manually trigger a "hide" event as a callback function in all the places where your first div is being hidden. For example:
$(".dismiss").click(function() {
$("#div-one").hide(function(){
$(this).trigger('hide');
});
});
Then you only have to have once the event handler:
$("#div-one").on('hide', function(){
//code that hides my second div
)};
Of course, you will have to manually add the trigger every place where relevant. So its not "the one solution".
you can use jquery dialog to achieve this functionality.
The short answer seems to be: jQuery does not emit events on hide.
In order to combat this, I have used the best solution I could find, and started an open project to enable sending of the required events: https://github.com/hypesystem/showandtell.js
This should cover, at the moment, the most common use-cases. Any feedback on this is appreciated.
try like this
$(".dismiss").click(function() {
$("#div-one").hide(function(){
$("#div-two").hide('slow');
});
});

JQuery if statement for .is(':visible') not working

I have a div that when the page is loaded is set to display:none;. I can open it up using this simple code:
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").css({'display':'block'});
});
Once it's opened, I'd like the user to be able to close it so I tried using the .is(':visible') function and then wrapping my original code in an if statment but this time using display:none;
if($('.field-group-format-wrapper').is(':visible')){
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").css({'display':'none'});
});
}
This does not seem to work though and I am not getting any syntax errors that I know of.
I also tried this:
if ($('.field-group-format-wrapper').is(':visible'))
$(".field-group-format-toggler").click(function () {
$(".field-group-format-wrapper").css({'display':'none'});
});
... but that did not work either.
You can just use the toggle function:
$(".field-group-format-toggler").click(function()
{
$(".field-group-format-wrapper").toggle();
});
This will show the '.field-group-format-wrapper' elements if they are currently hidden and hide them if they're currently visible.
FYI the reason your code snippet in your question wasn't working is because you're only checking the visibility of the elements on dom ready, rather than on each click - so the event handler to show the elements will never be attached.
I guess your function is only being called on page load at which time all divs are hidden.
Why not check the visibility in the click event handler?
$('.field-group-format-toggler').click(function(){
var $wrapper = $('.field-group-format-wrapper'); //Maybe $(this).parent()?
if($wrapper.is(':visible'))
$wrapper.hide();
else
$wrapper.show();
As already mentioned, you can use the toggle function to achieve what you want.
To add a bit of extra information, when attaching events like you're doing, you're actually using a subscription model.
Registering an event puts it in a queue of events subscribed to that handler. In this case, when you add the second event to change the CSS, you're adding an event, not overwriting the first one.
Whilst thing isn't actually causing your problem, it's worth being aware of.

jQuery event to detect when element position changes

I would like to know if there is a jQuery event that I can use to determine when a particular DIV's top property has changed.
For instance, I have invisible content above a DIV. When that content becomes visible, the DIV is shifted down. I would like to capture that event and then use the offset() function to get the X/Y coordinates.
The easy answer is that there are no events in the DOM for detecting layout updates.
You have a couple options the way I see it:
Poll, nasty but it may work depending on your update frequency requirements.
Tap into whatever event causes the invisible DIV to change size and do whatever you need to do in that handler
I shall correct myself.
I took a look at the DOM and noticed the DOMAttrModified event and found this JQuery Plug-In that you might be able to leverage to do what you want.
As the article mentions, it works great in IE and Firefox but seems to have problems in WebKit.
I thiiink you should be able to do:
$(document).ready( function (){
$("#mydiv").bind("movestart", function (){ ...remember start position... });
$("#mydiv").bind("moveend", function (){ ...calculate offsets etc... });
});
$("#someId").resize(function () {
// your code
});

Categories

Resources