I am creating an app using jQuery Mobile and PhoneGap.
I "delegate" a button on "tap" to perform some heavy processing and display a loading spinner. If users continue to tap on my app, the taps get queued up and fall through to be processed by the app after the heavy processing completes - and end up clicking on unwanted stuff.
How can I prevent this?
(From what I understand, stopImmediatePropagation doesn't help as these are new user events.)
Thanks
To inactive taps on the whole page you could overlay the whole page with a transparent div. Although it might be considered a borderline hack - this would actually use minimal js and css!
The caveat is that it would not give any visual indicator that the page is inactivated.
To do that one could, use a semitransparent gray for the overlay or, as I've done below, show a loading message.
First off, a small CSS discussion:
To make the div cover the whole page set width and height 100%. To position it correctly, use position:absolute and for the transparent background use an rgba background-color property (see below). You should also declare a z-index (can be increased if needed) to ensure that it goes on top of everything else and remove tap-callout using the -webkit-tap-highlight-color property. Set display to none and then show it during your heavy lifting.
I made a jsfiddle which hopefully clears things up.
Here I've made div with an id of "inactivator" which features the properties discussed above.
I've also made a button with an id of "inactivate" to which I've delegated jQuery's show function.
I also took the liberty to add jQuery Mobile's default loading message to show simultaneously just to give a visual indicator of the app thinking (so it's not mistaken for lag).
Here I've added a timeout function so that the loading message and "inactivator" hides after 5 second. Obviously in your case the same code should instead be fired upon completion of your "heavy processing" rather than after five seconds.
(New, additional answer since I didn't understand the question correctly but the old answer still might be helpful to other people.)
The easiest way I can think of is inactivating the button at the start of your javascript function and then reactivating it when it's suitable:
$('#YourButton').addClass('ui-disabled');
At the end of your function (or whenever you'd want it active again:
$('#YourButton').removeClass('ui-disabled');
So it took me a while to figure it out... you have to return FALSE from the delegate function for parent elements to ignore the event. The return false line below fixes my issue:
$(document).delegate("#finish", "tap", onFinish);
var onFinish = function() {
$.mobile.loadingMessage = "Finishing...";
$.mobile.showPageLoadingMsg();
setTimeout(function(){
HEAVYPROCESSING();
$.mobile.changePage($("#choosearticle"));
}, 50);
return false; // important - stops the two click fall through problem!
}
Related
I don't know if this is the effects of an update panel or what, but I basically have a drop down list that allows a user to select an item as a filter. When the item is selected it should bring back only one item into a grid view. That is this specific filter will at most bring back the record you are looking for. This works fine if the user clicks an "apply" link to apply the filter. Behind the apply link is some server-side code (C# within an ASP.NET Web Forms application).
We had a request by a user with something to the effect of:
"Why do I have to click the apply button if I make a selection in this
one drop down filter...it should simply get that one record I am
searching for. This helps me because I don't have to click the
"Apply" button."
I agreed with him and thought what is the easiest way to do this...I thought: Simple, I will have an on change event handler of the drop down such that when a selection is made I'll trigger a click event. Something to this effect:
$("#MainContent_ddlCompany").on("change", function() {
var companyId = $("#MainContent_ddlCompany").val();
$("#MainContent_hdnCompanyValue").val(companyId);
$("#<%=ddlCompany.ClientID %>").trigger("chosen:updated");
if (companyId.length > 0) {
$(".apply").click();
$(".apply").removeClass("applyButton");
$(".apply").addClass("resetButton");
} else {
//cleared selection of a company
$(".apply").removeClass("resetButton");
$(".apply").addClass("applyButton");
}
});
At first this didn't work, and I couldn't tell why, but then after some serious googling I changed this line:
$(".apply").click();
To this:
$('.apply')[0].click();
That worked great...so I decided to test it some more. As I kept selecting one filter value after another I noticed the page started to slow down. In fact by the 6th or 7th time it was pretty unusable. I don't know why it's happening, but I suspect again it has to do with the fact that this linkbutton with the class name .apply is inside an update panel.
But still I thought to myself, it was inside of an update panel before I changed my jQuery code to simulate the click event. So why does the page slow down and drag with this little piece of code? Is calling the event from jQuery code rendering something else in the HTML that could be causing this?
If I change my code back and force the user to click the apply button then we are back to a good normal speed. Why is it if I tell jQuery to simulate clicking the button my page slow down? It's doing the same thing, the simulation of the click of this link button is calling its server-side code method whether the user clicks it or I have jQuery click it.
For now I'm at a loss as to why this is happening because this button is in an update panel in either case, yet when I have jQuery click it via $('.apply')[0].click(); the page slows down after several attempts. Yet when I have the user simply click this button (without the jQuery click event) then it works fine?
What am I missing here?
Ugh, well, I found my issue. Because I was using updatepanels I had to wrap my jQuery code to include an add_endRequest. That is, you have something to the effect of:
$(document).ready(function() {
//Some initial event/triggers
var prm = Sys.WebForms.PageRequestManager.getInstance();
prm.add_endRequest(function () {
//Copy of some initial event/triggers
});
});
Why do I use the endRequest you ask? Well, because updatepanels basically throw away all your events after an asynchronous postback because the HTML at that point (after an update) is rendered again and at that point all events associated with any control inside an update panel are wiped away. At this point of course document.ready() does not run, so I have to resubscribe to these events inside of endRequest. Enter my issue...
I had a huge brain fart where I basically took everything, literally everything inside document ready and copied it into endRequest. In fact, if I remember correctly, I read articles which stated
Whatever you have in document ready simply copy paste into endRequest
That's fine, but you have to be careful here. I was throwing in events that were not wrapped around inside of an updatepanel into endRequest. The result is disastrous...at least for me.
These events would be attached then multiple times..or based on the number of asynchronous postbacks made. In my case, as I was testing I mentioned after the 6th or 7th time performance starts degrading. Well, by that time my controls were being attached that many times to events. For instance, my .apply button along with my dropdownlist were both outside of my updatepanel. But my jQuery code was attaching the change event of my dropdownlist in both document ready and endRequest.
The result is initially it's pretty fast, because it's only in document ready. But as I make asynchronous postbacks these events are being attached every time. For n tests I would have n attached events...in my case the test of 7 yields 7 on change event handlers!
Case in point, do not place any event handlers such as jQuery's on() event for any controls that are NOT inside an update panel. Otherwise you will run into what I ran into which was poor performance as events are happening.
I am trying to create a simple 3 image slideshow with a next and back button in Flash CC using HTML5 Canvas. I'm new to javascript and seem to be having an issue with it working.
this.stop();
this.next_btn.addEventListener("click", playClickNext.bind(this));
function playClickNext()
{
this.gotoAndStop(this.currentFrame + 1);
}
this.back_btn.addEventListener("click", playClickBack.bind(this));
function playClickBack()
{
this.gotoAndStop(this.currentFrame - 1);
}
I'm getting it to publish and the next button works but sometimes goes to the wrong frame. The back button sometimes work and sometimes doesn't. The most common thing it does is also go back to a random frame when clicked.
Thanks for any help!
I put together a quick sample, and got a similar result. The issue for me was that the frame that the script was on would fire any time you went to that frame. This meant that the listeners on the button would pile up, and fire multiple times.
The ideal solution is to pull the code out of the FLA, and into your HTML/JS app. You can target the timeline directly using the instance names. For example, in my app, it is all on the main timeline, so you can use:
exportRoot.next_btn.addEventListener("click", handler);
To solve it without rearchitecting, you could also just ensure that either:
Your frame with the script is never navigated to. You could make your "first" frame 2 frames long, and put the code on frame 1, and the stop() on frame 2. Then just ensure you never gotoAndStop on 1. You will have to put restrictions on both previous and next, because you can gotoAndStop at a higher frame than the max, and it will wrap.
Remove all the event listeners each frame. The way you set up your listeners using bind is problematic for removal, so I recommend just removing all listeners.
I uploaded a quick sample here [ https://dl.dropboxusercontent.com/u/2224806/Nav.fla ] that uses the second approach, mainly because it is an easy fix.
Sorry, link expired
Let me know if this solves your issue, or if it is related to something else.
I need a few objects on my pages to animate out when a user clicks a link. I want each object to scale and fade out but not all objects such as the navigation buttons.
I was thinking that upon a user clicking a link, the page delays 1 second before opening the redirecting the link to allow fade out giving the animation time to take effect.
Look at the JS event window.onbeforeunload
https://developer.mozilla.org/en-US/docs/Web/API/window.onbeforeunload
It will hopefully be enough to just run the exit animations when this function is triggered - it generally takes the browser around a second to unload the page completely but this varies depending on your browser, page size and cpu speed.
Assuming you're using plain JS and you know how to do CSS transitions, the simple way to make animations occur on page exit is something like this:
window.onbeforeunload = function(e){
document.getElementById('myDiv').className = 'out';
}
Where myDiv id the element you want to animate and out is the CSS class representing the final stage of your transition.
Here is a JSfiddle: http://jsfiddle.net/X5vKS/
If you need finer control over the wait time, you could use the onbeforeunload function with setTimeout to delay the page exit by the length of time of your animation. This is slightly complex for a JS beginner but is quite doable.
Okay, so my problem is that I want to make a page flip transition into this custom url that I am creating through javascript.
$("#btnSave").click(function(e){
e.preventDefault();
window.location = "mypage_with_params.html?" + selectedParams; //selectedParams is a list
return false;
});
What function can I call to make the window fire into this url with a page flip animation?
This is actually a pretty hard thing to do. Transitions between pages don't really work, though it's OK to transition to things on the same page, perhaps using AJAX.
I wrote a little bit up on how to do a basic flip here: http://css3.bradshawenterprises.com/flip/, though browser support isn't that great.
In order to simulate a page transition, you'd need to load the new page via AJAX into a div behind the current one, then flip from one to the other, then likely reset everything using JS.
I intend to have a busy waiting spinner as per shown in this post: How to show loading spinner in jQuery? - since it's the cleanest and easiest to implement:
$('#loadingDiv')
.hide() // hide it initially
.ajaxStart(function() {
$(this).show();
})
.ajaxStop(function() {
$(this).hide();
})
;
and couple it up with the animations from http://www.ajaxload.info/
I have multiple "views" in my page i.e., different parts of my page can be updated/modified with the corresponding ajax calls.
I'd like to "position" this #loadingDiv close to where the 'action is' so to speak. How should I go about doing it?
Few things that come to mind:
Have multiple divs all over the place and have them hidden and show them per element as per the corresponding action - seems naive and wasteful
Have an empty <span></span> to 'hold' the #loadingDiv - show it when something happens in that span. I don't know how to do this though...
Something else??
Basically how best to approach positional busy waiting/signalling rather than have a single global one? Or is the single-global option preferred i.e., To have a "fixed" div, hidden show up on the top/center of the page for every activity?
Just want to know which option most of you have used/preferred to tackle something like that and how do you suggest I go about it...
There are some things to consider:
Can the user cause problems if he interacts with other parts of the page while the request is loading?
In this case block the whole UI with a lightbox like transparent overlay.
Are the actions tiny and small, irrelevant to the rest of the application?
Use the local, positioned spinner. If it's a button, change the button's contents from eg. "Save row" to "Saving..." with a spinner.
If the request is significant but you want to let the user to mess around, and the GUI is complex
You can overlay only parts of the screen. http://sfiddle.net/Lv9y5/39/
Local updates, the non blocking way
Use jQuery.position to create a Spinner object from scratch. It should have a .show(node) and .hide() method, and maybe a .setMessage(txt);
The DOM reference passed to the show method is the element the user clicked on. With the reference, you can use jQuery .position() to determine where to absolute position the loading div. The loading div should be placed after the BODY element.