How to detect/query currently ongoing changepage / transition in jQuery Mobile? - javascript

How can I detect/query, whether a changepage / transition is currently ongoing in jQuery Mobile (jQM)?
My goal is to prevent opening a dialog when a switch between pages is currently happening. Currently, th UI breaks when a dialog opens while a changepage event is ongoing.
Any Ideas on that?

In JQM 1.4.0 the class ui-mobile-viewport-transitioning is added the body tag during transitions so the following works for me:
if (!$("body.ui-mobile-viewport-transitioning").length) {
//do something
}else{
console.log("Don't do it we are transitioning")
return false
}

pagebeforeshow
Triggered on the page being shown, before its transition begins.
pagebeforehide
Triggered on the page being hidden, before its transition begins.
pageshow
Triggered on the page being shown, after its transition completes.
pagehide
Triggered on the page being hidden, after its transition completes.
http://jquerymobile.com/demos/1.0a4.1/#docs/api/events.html

I didn't find transition state information within jQuery Mobile, however you can simply add three events, one before and one after the change and one for when the page change has failed. Then, keep in a global variable whether a transition is currently occuring:
window.transitioning = false;
$(document).on("pagebeforechange", function() { transitioning = true; });
$(document).on("pagechange", function() { transitioning = false; });
$(document).on("pagechangefailed", function() { transitioning = false; });
In this example, I create a global variable transitioning but you could create this variable where you see fit, e.g. making a local variable in your dialog handler rather than using a global variable.
You can now add your condition like this:
if(!transitioning) {
// Do your stuff
} else {
// Delegate the events by listening for pagechange and then do your stuff
}

Related

fullpage.js: disable page scroll when scrolled with the mouse pointer inside a container

What's happening: Scrolling works no matter which position i have the mouse while i scroll.
What i want to achieve: When the user scrolls with the mouse pointer positioned inside a particular container, I would like to disable the plugin from changing pages. When the user scrolls with the mouse pointer outside that same container, the normal functionality of the plugin should be restored; i.e. the pages should be scrollable again.
What have i tried: I listened for the scroll event on the document and found out whether the mouse is inside the container while executing the scroll and store the possibilities as a boolean.
$(document).bind("mousewheel", function(event) {
// preventScroll = true;
console.log(event);
if($(event.target).closest(".no-scroll").length) {
preventScroll = true;
}
else {
preventScroll = false;
}
});
Then onLeave i try to find out the value of preventScroll and try to stop event propagation (since in want to stop an actual event) by returning false
setTimeout(function() {
console.log(preventScroll);
if(preventScroll) {
console.log("no-scroll")
return false;
}
}, 10);
I an using setTimeout to capture the desired value of preventScroll although I guess the plugin executes a scroll within that 10 ms and that's why return false doesn't seem to have an effect. I can't seem to figure out how else to proceed to achieve the desired functionality.
Codepen: https://codepen.io/binarytrance/pen/YxBqPj
In this implementation, the container i want to disable scroll is in the second page/section. Please be aware of the values spit out in the console.
Use the fullpage.js option normalScrollElements. Check the fullpage.js docs for more info:
normalScrollElements: (default null) If you want to avoid the auto scroll when scrolling over some elements, this is the option you need to use. (useful for maps, scrolling divs etc.) It requires a string with the jQuery selectors for those elements. (For example: normalScrollElements: '#element1, .element2'). This option should not be applied to any section/slide element itself.

How I can I delay the expansion of a collapsible until its content is fetched?

I want to integrate a collapsible in my jQM app (1.3.2) that works as follows:
It starts collapsed.
On click it starts to fetch listitems for the collapsible from the server. The collapsible stays closed, a load icon may be spinning.
After all elements are loaded and the listview refreshed and ready, the collapsible expands.
If you click it again, it closes directly without delay, and starts from 1.
My initial idea was to grab the expand event and prevent its propagation. When loading is finished I de-register my custom event handler to turn the collapsible back to normal, and finally triggering the expand event from JavaScript to open.
The problem is that this works for the first round, but afterwards the collapsible opens anyway. Consider this example (also in jsfiddle):
<div data-role="collapsible" id="c">
<h2>Collapse</h2>
<ul data-role="listview" id="lv">
<li>You don't see me!</li>
</ul>
</div>
<a href="#" data-role="button" onClick="$('#c').off('expand', stop)">
Unlock the collapsible
</a>
<a href="#" data-role="button" onClick="$('#c').on('expand', stop)">
Lock the collapsible
</a>
JavaScript:
var stop = function(event) {
alert('Locked!');
event.preventDefault();
}
// executed after the element is created ...
$('#c').on('expand', stop)
Here, if you click the collapsible after loading, it stays closed (good). When you click unlock, it opens and no alert is shown (good). If you lock it again it shows the alert (good) but opens anyways (bad).
Can anyone enlighten me what I am doing wrong? There seems to be side-effects from unlocking that I cannot see. There are several similar questions here, but most are happy with just preventing the expansion without turning it back on again.
So my question is: What is the best way to delay the expansion of a collapsible reliably?
Edit: I added another example that integrates the listview logic and also shows this error. jsFiddle is here. I also moved to .one() to make the de-registering more traceable.
New JavaScript:
var stop_n_load = function (event) {
alert('stop opening! (or at least try to ...)');
// try to stop the expanding by stopping the event
event.preventDefault();
// Do some work that takes time and trigger expand afterwards ...
setTimeout(function (e) {
dd = new Date();
$('#lv').empty();
$('#lv').append("<li><a href='#'>Item A ("+dd+")</a></li>");
$('#lv').append("<li><a href='#'>Item B ("+dd+")</a></li>");
$('#lv').listview('refresh');
// when done, start expanding without this handler (was register only once)
$('#c').trigger('expand');
// for the next collapse register stop_n_load again
$('#c').one('collapse', reset);
}, 2000);
};
var reset = function (event) {
$('#c').one('expand', stop_n_load);
};
$('#c').one('expand', stop_n_load);
On the first expansion it works as expected, it first updates and then opens. On the second run it opens without waiting, you can see the timestamp updating later, so the event is called properly.
There seems to be a problem with .preventDefault() I don't understand ...
When expand/collapse events are triggered, jQM adds several classes to show/hide contents as well as update collapsible button/header.
In this case, event.preventDefault() isn't going to prevent event(s) from bubbling and stop them from executing. Thus you need to use event.stopImmediatePropagation() to stop expand event once header is clicked.
When a collapsible is collapsed, it has a class ui-collapsible-collapsed and once expanded, the class is removed. Therefore, when collapse is triggered, it works normally without interruption.
$(".ui-collapsible-heading-toggle").on("click", function (e) {
// check if collapsible is whether expanded or collapsed
if ($(this).closest(".ui-collapsible").hasClass("ui-collapsible-collapsed")) {
// true
// stop "expand" event on click,tap,vclick,touchstart, etc...
e.stopImmediatePropagation();
// show loading msg - optional
$.mobile.loading("show", {
text: "Loading...Please wait",
textVisible: true
});
// for demo
// add items, expand and hide loading msg
setTimeout(function () {
$("#lv").empty().append(items).listview("refresh");
$("#c").trigger("expand");
$.mobile.loading("hide");
}, 1000);
}
// false
// collapse normally or do something else
});
Demo
I'm not intimately familiar with JQM but my first thought was empty the list before sending for more data....which seems to prevent it expanding.
var stop = function(event) {
$(this).find('.ui-listview').empty()
event.preventDefault();
/* do ajax and then refresh component with expand enabled */
}
$('#c').on('expand', stop);
DEMO - without ajax
After wading around in the event system, I concluded for now that if you set preventDefault() once you are stuck with it forever. Based on the solution of charlietfl I added code to make the visual impact minimal, maybe there is also a way to prevent the GUI from refreshing for the 20ms to make it fully unnoticable.
var stop_n_load = function (event) {
$('#lv').empty();
$('#lv').listview('refresh');
setTimeout(function() {
$('#c').trigger('collapse');
}, 20);
// Do some work that takes time and trigger expand afterwards ...
setTimeout(function () {
dd = new Date();
$('#lv').empty();
$('#lv').append("<li><a href='#'>Item A (" + dd + ")</a></li>");
$('#lv').append("<li><a href='#'>Item B (" + dd + ")</a></li>");
$('#lv').listview('refresh');
$('#c').trigger('expand');
$('#c').one('collapse', reset);
}, 2000);
};
I'm still searching for a solution that resets the event to its previous state, though.

Jquery bug dblclick/click

I've been experimenting with jquery and have come upon a problem. http://javascript.nicklewers.co.uk/nav/
On the 'Android' tab I've set it so that if you click once, the sub menu will appear, however when doubleclicking, the main content will appear.
Now the problem is: a double click involves two single clicks which involves the sub menu opening and closing very quickly and this looks bad. How do I prevent this?
try this (using a timer to know if single click or double):
alreadyclicked=false;
$('#android').bind('click',function(){
var el=$(this);
if (alreadyclicked)
{
alreadyclicked=false; // reset
clearTimeout(alreadyclickedTimeout); // prevent this from happening
// do what needs to happen on double click.
}
else
{
alreadyclicked=true;
alreadyclickedTimeout=setTimeout(function(){
alreadyclicked=false; // reset when it happens
// do what needs to happen on single click.
// use el instead of $(this) because $(this) is
// no longer the element
},300); // <-- dblclick tolerance here
}
return false;
});
Try a delay before showing/hiding the submenu. With JS this can be done using the setTimeout() function.
$('#android').dblclick(function(){
$('.content, #subnav').toggle();
});
why do you toggle both the content and the submenu? Removing it is not going to solve the problem, but I think it should not be there.

check for ('div')mouseenter on ('a')mouseleave

my problem is following:
I got a trigger(a) and a popup(div). The div doesn't lie nested inside the anchor.
When I hover over a, I want the div to show up.
When I go from a to the div, I want it to stay visible.
When I leave the div, I want it to close.
When I hover over a and leave without entering the div, I want the div to close.
I got most of that figured out, but now I'm struggeling with requierement no. 2.
When checking for mouseleave on a, I check if there is a mouseenter on the div. If it is, I want to abort the mouseleave. If not, I want to close the div.
What am I doing wrong? Is this even the right way to do this?
Here's the markup:
<a href="#" class="popup_toggle" style='display:block;width:50px;height:50px;border:1px solid red;position:relative;'>Toggle</a>
<div class="popup_div" style='position:absolute;top:50px;left:0px;border:1px solid blue;display:none;'>Popup</div>
Here's the jQuery:
$('.popup_toggle').mouseenter(function() {
var element = $(this).next('.popup_div');
$.data(this, 'timer', setTimeout(function() {
element.show(100);
}, 500));
});
$('.popup_toggle').mouseleave(function() {
clearTimeout($.data(this, 'timer'));
if($('.popup_div').mouseenter==true)
{
return false;
}
else
{
$('.popup_div').hide(100)
};
});
What you're trying to do is fairly simple. When entering the trigger, identify the panel (layer, popup, whatever), save reference to each other using .data() and have the event handlers check if the related targets are either the trigger (from the panel view) or the panel (from the trigger view). I threw something together. Have a look at the console log to see how this works… http://jsfiddle.net/rodneyrehm/X5uRD/
That will most likely not work...no. I would suggest that you add a mouseenter and mouseleave callback to you <div> element as well and have them set a global variable that tells your other callbacks how to handle their events, i.e. "if global variable is true, don't hide the popup on mouseleave, otherwise hide popup" or something like this.
The other approach would be to check whether the mouse is inside the popup when the mouseleave callback tries to hide the popup. That might be much more work than it is worth though.
I believe the problem with your implementation is that the mouseenter on the div will fire shortly after the mouseleave from the a.
This would give you something like:
$('.popup_toggle').mouseenter(function() {
// Clear any pending "hide" timer
// Set a show timer
});
$('.popup_toggle').mouseleave(function() {
// Clear any pending "show" timer
// Set a hide timer
});
$('.popup_div').mouseenter(function() {
// Clear any pending "hide" timer
});
Note that you'll have to make sure that you access the same timer from both the .popup_toggle event and the .popup_div event. You may want to consider using Ben Alman's doTimeout plugin to help with this. It (usually) results in much clearer code than manually working with setTimeout/clearTimeout.

Stop duplicate mouse over detection

I have a popup that is executed on mouseover with jquery.
Within that function I have a second delay before the popup displays using settimeout
Problem is if in that second they mouse over multiple times then multiple popups are triggered.
$('#div').mouseover(function() {setTimeout("popup()",1000);});
What I need to do is disable the detection and then re enable it in popup().
How might I do that?
You can use .hover() with a clearTimeout(), like this:
$('#div').hover(function() {
$.data(this, 'timer', setTimeout(popup, 1000));
}, function() {
clearTimeout($.data(this, 'timer'));
});
This clears the timeout you're setting if the mouse leaves, you'll have to stay on the element for a full second for the popup to trigger. We're just using $.data() on the element to store the timer ID (so we know what to clear). The other change is to not pass a string to setTimeout() but rather a reference directly to the function.
I guess something like this
(function(){
var popup_timer = 0;
$('#div').mouseover(function() {
clearTimeout(popup_timer);
popup_timer = setTimeout("popup()",1000);
});
});
EDIT updated code, clearTimeout added, wrapped

Categories

Resources