Showing posts with label Mobile. Show all posts
Showing posts with label Mobile. Show all posts

Saturday, June 7, 2014

jQuery show() and hide() Don't Seem to Work with jQuery Mobile

That's not entirely true. Chances are you're running into this type of situation.

I was trying to hide a set of ui-block-* elements in a JQM grid when the user tapped a button. On the same set of ui-block-* elements, my media query for an iPhone was setting display: block !important. When the user would tap the button the elements would animate to hide but then suddenly re-appear.

This was not a problem on an iPad or in a desktop version of Chrome. But on the iPhone (iOS 6 and 7 at least), the !important directive was winning -- was dictating the final display.

That !important directive was unnecessary in my case, so when I removed it the calls to hide() and show() began working as expected.

Saturday, March 3, 2012

Loading and Refreshing the Application Cache of Multiple Sites

THE PROBLEM:

We have multiple web applications that are offline-capable using the HTML5 Application Cache feature. We want to give users the ability to go to one location to load / refresh / update / synchronize all applications and avoid the need to go to each and every application individually. Details of interest: we are only targeting the iPad 2 (or higher) and each application is not guaranteed to be on the same domain or sub-domain.

THE SOLUTION:

The HTML5 Cross-Document Messaging feature + an iframe + jQuery. Certainly feels little goofy, but it works.

THE DETAILS:

At least at this stage, there doesn't appear to be a way to determine whether the Application Cache of a specific site is up-to-date without loading that site. Said another way, we don't know if a site's Application Cache contains stale data until we load that site, and as soon as that site is loaded, it will begin updating, if necessary. The user must then wait until the browser is done fetching and storing the resources for the cache to actually be ready for offline use.

Therefore, if we could come up with a way to visit each site for the user, we could guarantee the newest manifest file would be checked and the cache updated.

There are several pieces to this puzzle.

1) One site, which we'll call "the loader," with the list of URLs for the target sites in a JavaScript array. I injected that list into the page server side, but they don't need to be. These are the individual web applications using the Application Cache so they can be available offline.

2) An iframe on the loader, which can be visible or hidden.

3) The individual web applications will each need a function to listen for requests.
function crossDocumentListener(event) {
//NOTE: There's no security in place here! Educate yourself before using!
if (event) {
var response = "origin:" + event.origin + ";request:" + event.data + ";location:" + window.location + ";response:" + window.applicationCache.status;
event.source.postMessage(response, '*');
}
}
window.addEventListener("message", crossDocumentListener, false);

I'm returning the origin, the original request data, the site's location, and the application cache status. The latter is what I want most.

4) A listener on the loader to get the responses. It is set to message the site loaded in the iframe every 2 seconds.

function responseListener(event) {
var response = event.data;
displayResponseVisible(event);

// 0 = uncached, 2 = checking, 3 = downloading
if(response.indexOf('response:0') > -1 || response.indexOf('response:2') > -1 || response.indexOf('response:3') > -1) {
if(!intervalId) { // We only set it once.
intervalId = setInterval(
function() {
getStatus();
}
, 2000);
}
}
// 1 = idle, 4 = updateready, 5 = obsolete
else {
if(intervalId) {
clearInterval(intervalId);
intervalId = undefined;
}

if(nextUrlIndex == -1) {
// The array is empty. We're done.
$('#message').prepend(completeMessage);
}
else{
// There are more URLs in the array to process so load the next one.
$.when(loadUrl(nextUrlIndex)).then(function() { getStatus(); });
}
}
}
window.addEventListener('message', responseListener, false);

5) More code to glue it altogether. Here are the key bits.

function getStatus() {
document.getElementById("app").contentWindow.postMessage('status', '*');
}

function loadUrl(arrayIndex) {
// Set the URL on the iframe, triggering it to load.
var iframe = $('#app');
$(iframe).attr('src', urls[arrayIndex]);

// Set the nextUrlIndex appropriately.
if(arrayIndex + 1 == urls.length) nextUrlIndex = -1;
else nextUrlIndex = arrayIndex + 1;

// http://www.elijahmanor.com/2011/02/jquerydeferred-to-tell-when-certain.html
var deferred = $.Deferred();
iframe.load(deferred.resolve);

return deferred.promise();
}

$(document).ready(function() {
$('#loader').click(function(event) {
$.when(loadUrl(nextUrlIndex)).then(function() { getStatus(); });
event.preventDefault();
});
});

I'm not crazy about the design of this, but with the glue code in place the details can be refined. As I refine it I'll try to update this post. If you have another way to approach this that you believe is somehow an improvement, I'd love to hear it.

Resources:

Sunday, February 5, 2012

iOS, Private Browsing, and the HTML5 Application Cache

This problem makes sense, but it caught me off-guard during debugging.

If you have a web application that uses the HTML5 Application Cache feature, the cache will not function properly with iOS Safari's Private Browsing setting on. The various JavaScript snippets that help with debugging will catch the most general error, which doesn't tell you much.

So if you have an app that seems to be caching properly in your desktop browser but not your mobile device, be sure to check your privacy settings!

Thursday, May 12, 2011

Getting Capabilities from WURFL in ASP.NET

This is one of those things that took me a little too much time to find the first time around.

I'm using WURFL with 51Degrees for mobile device detection in an ASP.NET MVC application. I needed to also detect whether the device using the app is a tablet or not. WUFL has the "is_tablet" capability defined, but this is not a default property on the Browser object.

So how do you get at the value for the current request? It's pretty simple once you see it:

Request.Browser["is_tablet"]