Some great tools I found useful for building Phonegap Apps

Sometimes the world of the twitter feed moves a lightning speed. Lots of good information is shared at any given moment in time and then lost to the news feed that has moved on from past updates to more recent ones. I thought It would be good to share some cool items I found while on the twitters for mobile app/ web development.

  • Onsen CSS Components - great CSS components for mobile built on top of Adobe topcoat that look just like iOS 7 that can be used in HTML5 hybrid apps (phonegap) or mobile web.
  • Onsen UI - Front end UI framework created with AngularJS and Topcoat to help you achieve that native like experience. Think smooth screen transitions.
  • ionic framework - Another mobile targeted library and UI kit for mobile optimized HTML,CSS and JS hybrid apps. Also built with Angular JS.
  • Marvel App - Free mobile and web prototyping tool. Draw mockups, take a photo of them and then link up the images into a functioning prototype. Share the mocked up prototype with a link.

Safari issue with showing loading graphic on click

Untitled Document

Life as a web developer means checking your code in all browser environments . The unfortunate reality is that sometimes we do not do this and eventually come across something that does not work in one browser by accident. Recently I had an issue where Safari on a Mac was causing issues.

The issue was reported that the standard design pattern of prompting the user from a link click or form submission with a loading indicator like an animated GIF with javascript was not working in the most recent version of Safari on Mac. Upon testing the issue reported was correct and the loading graphics were not showing.

Here is what the code looked like that did not work in Safari.

 

view plain print about
1function goToLink(){
2        
3        $('#somediv').html('<img src="loading.gif" > Loading...');
4        window.location.href = 'link.html?';    
5            
6    }

The issue I found is that Safari for whatever reason does not allow or show DOM manipulation after a link is clicked or form is submitted. To fix the issue you need to delay processing to allow time for the DOM to be changed to show the loading image and then execute the browser change location. Here is an example of how to do this...

 

view plain print about
1function goToLink(){
2        
3        $('#somediv').html('<img src="loading.gif" > Loading...');
4        setTimeout(function(){
5            window.location.href = 'link.html?';    
6        },1000);
7
8            
9    }

 

 

 

Using Modernizr to Detect HTML5 Feature Support

The problem: you want to use HTML5 features or libraries on your site, but you also want to support older browsers. Fortunately there is a library called Modernizr to help. Because HTML5 is not simply one thing, it is not possible to simply detect HTML5 compatibility as a whole, and using browser specific checks is an archaic practice that is not a solid solution because some browsers do have partial HTML5 support which means those users would miss out on content regardless of browser support. Instead, the proper method is to detect support for specific features of HTML5. Modernizr makes this process easy.

An example of this would be HTML5 video. In order to use Modernizr to check for this it is very simple.

Example index.html:

view plain print about
1<!-- Place the include in the head after any CSS includes. --> <!-- This should in the head because HTML5 Shiv (included library to enable HTML5 elements in older versions of IE) must execute before the body and if the page uses any CSS from Modernizr this will prevent a "flash of unstyled content." -->
2<head>
3    <script src="modernizr.min.js" type="text/javascript"></script>
4</head>

Example script.js:

view plain print about
1if ( Modernizr.video )
2{
3    if ( Modernizr.video.webm )
4    {
5        // Try WebM
6    }
7    else if ( Modernizer.video.ogg )
8    {
9        // Try Ogg
10    }
11    else if ( Modernizr.video.h264 )
12    {
13        // Try H.264
14    }
15}
16else
17{
18    // No HTML5 video supported, at this point you can use a flash player or another solution.
19}

Modernizr works for many other HTML5 features as well miscellaneous things such as CSS3, geolocation, SVG, WebGL, and more. For more information and to download Modernizr, visit http://modernizr.com/.

Sorting JavaScript Arrays

Here are some quick tips for sorting array values in JavaScript.

Be aware that using the sort() method on an array in JavaScript will temporarily convert elements in the array to strings and then sorts them in alphabetical order.

To sort using some other method, you need to pass in a comparison function as an argument to sort(). This function decides which of its two arguments should appear first in the sorted array. This function decides which of its two arguments should appear first in the sorted array. If the first argument should appear before the second in the sorted array, the comparison function will return a number less than zero. If the first argument should appear after the second in the sorted array, the comparison function should return a number greater than zero.

So, for example, to sort array elements into numerical rather than alphabetical order, you might do this:

view plain print about
1var a = [33, 4, 1111, 222];
2a.sort; // Alphabetical order: 1111, 222, 33, 4
3a.sort(function(a,b) {
4 return a-b;
5}); // Returns numerical order: 4, 33, 222, 1111

Another example of sorting items would be to perform a case-insensitive alphabetical sort on an array of strings by passing a comparison function that converts both of its arguments to lowercase (with the toLowerCase() method) before comparing them:

view plain print about
1a = ['ant','Bug','cat','Dog'];
2a.sort(); // =>
case-sensitive sort: ['Bug','Dog','ant','cat']
3a.sort(function(s,t) {
4 var a = s.toLowerCase();
5 var b = t.toLowerCase();
6 if(a < b) return -1;
7 if(a >
b) return 1;
8 return 0;
9}); // => ['ant','Bug','cat','Dog']

Javascript Dates and server-side processing

Sometimes, just because you CAN do something doesn't mean that you should. Really - I know that it's super cool to be able to convert dates to milliseconds and do all sort of math computations around them, but sometimes, just sometimes, it's better to take a step back and think about the reason that you need to take the most complicated route to the end and maybe there might be a simpler way.

I just spent quite a bit of time troubleshooting an issue with ajax calls not returning data in a calendar-based interface, where the user would choose a calendar month and it would display marketing campaigns scheduled to go out in that month on their appropriate day.

Our users reported issues of the calendar being blank (not showing any campaigns) on the upcoming month.

The first problem we had was that no one in the office could reproduce the reported issue. After going through the standard cookies, session, browser, operating system checks, we still could not figure out why everyone in our office could see the monthly campaign data on the calendar yet none of the reporting users could. I noticed a pattern that all of the users reporting the issue were in California and all of the in-office testers were in New York.

Mystery number 1 was solved - the problem was a time zone issue. I changed the local time zone on my computer and could immediately reproduce the user's reported behavior.

After that issue had been solved, I then had to figure out why different values were being passed for the start date in the ajax requests from different time zones. The start date was something calculated by JavaScript and sent through as a parameter to an ajax request to do a lookup in the database.

Here is how the start date was being set up in the JavaScript code for the calendar:

view plain print about
1date = new Date(); // get current date
2var start = cloneDate(date, true); // clone the date to a new variable
3start.setDate(1); // set the date to the first day of the month
4
5// save start date to ajax param
6data[startParam] = Math.round(+start / 1000); // some math conversion to convert date to milliseconds

For a start date/time of Sun Sept 1 00:00:000 EDT 2013, this is the value that was being passed: startParam: 1378008000 = data[startParam] = Math.round(+rangeStart / 1000);

For a start date/time of Sun Sep 1 00:00:00 PDT 2013 , this is the value that was being passed: startParam: 1378018800 = data[startParam] = Math.round(+rangeStart / 1000);

A difference of 10800.

We also got different values for Central and Mountain Time.

On the server-side processing script, the numeric value was being converted back to a date so that the month number of the value could be determined:

view plain print about
1<cfset eventMonth = month(DateConvert("utc2Local", Replace(Replace(DateAdd("s", url.start, "January 1 1970 00:00:00"), "{ts '", ""), "'}", ""))) + 1>

The problem with this approach is that the server only has one time zone and could not adjust for different values being passed in from the Javascript.

The easiest solution here is the best - rather than doing complex math calculations to send through numeric values that no one but a computer can read or debug, I just got the current month value in Javascript (which is always the same for a date no matter what the time zone is) and passed that through in the ajax request and didn't have to do any calculation on the server side to get the value that was all we were really needing to get in the first place.

view plain print about
1eventMonth = start.getMonth() + 1; // add one because it's zero based (need the month in coldFusion numeric range 1-12)

Once we had that eventMonth in the JavaScript, it was easily passed through as a parameter in the ajax request and we got the same results in all time zones.

How to fallback from CDN to local jQuery

External dependencies are, in fact, external. As such, they are calculated risks with tradeoffs. CDNs are great, but for those minutes or hours that they go down a year, they can be very annoying.

How can we as application developers fallback gracefully when an external dependency like a CDN goes down?

With JavaScript we can detect when our CDN-hosted JavaScript resources like jQuery or jQuery UI aren't loaded successfully and try again to load them from local locations.

One way to create a CDN fallback is to check for a type or variable that should be present after a script load. If that variable is not there, try getting that script locally.

Note the important escape characters within the document.write. Here's a jQuery example:

view plain print about
1<script src="http://ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
2<script>
3if (typeof jQuery == 'undefined') {
4 document.write(unescape("%3Cscript src='/js/jquery-2.0.0.min.js' type='text/javascript'%3E%3C/script%3E"));
5}
6</script>

Or you can do something like this:

view plain print about
1<script src="//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min.js"></script>
2<script>window.jQuery || document.write('<script src="js/jquery-2.0.0.min.js">\x3C/script>
')</script>

You can also use RequireJS, which has a great shorthand for fallback URLs:

view plain print about
1requirejs.config({
2 enforceDefine: true,
3 paths: {
4 jquery: [
5 '//ajax.aspnetcdn.com/ajax/jquery/jquery-2.0.0.min',
6 //If the CDN location fails, load from this location
7 'js/jquery-2.0.0.min'
8 ]
9 }
10});
11
12//Later
13require(['jquery'], function ($) {
14});

As always, plan for the worst and hope for the best!

Compatibility Cheatsheet for Web Developers

This is a great resource for checking compatibility tables for support of HTML5, CSS3, SVG and more in desktop and mobile browsers.

Bookmark it!

http://caniuse.com/

FOR Loops Overwriting Ajax Responses (and how to fix)

I encountered a problem today with a FOR loop inside of a JavaScript function. This for loop makes Ajax requests (using jQuery 1.8.1) and populates a specific div, based on where we are in the loop, with the results of the call. However, we noticed that it stopped updating all of the divs and appeared to populate the last one multiple times.

[More]

PEMDAS

The title above refers to order of operations in Math. I remember it by "Please Excuse My Dear Aunt Sally" but your teachers may have taught you something different. For those of you who didn't like math it means handle Parentheses first, then Exponents, then Multiple or Divide going left to right, then Add or Subtract going left to right.

This blog is actually about what you can and can't nest in code. I was always a little bit of a math nerd so I thought the blog is about order so I can do an intro about order of operations, I know it's a stretch, but it got your attention didn't it? Anyway, I recently had a fight with a cfform in particular a cfinput of type datefield. I could not get the JS of the calendar to render. I had a table to aid with the formatting of the form variables.

[More]

Developing Chrome Extensions - What you should know

Recently, I've delved into the world of Chrome extensions, partially because I'm interested in learning the technology, but more because the add-ons I wanted just didn't exist yet. If you're interested in getting started but want to know what you'll need to get moving I'd recommend the following resources:

https://github.com/EdGuiness/date-picker/wiki/What-I-learned-from-writing-my-first-Chrome-extension -This is a general explanations of some little things to look out for while developing and states what you'll need to get started.

http://developer.chrome.com/extensions/getstarted.html -The Chrome extensions development page for getting started. A required resource for specific questions including code samples.

http://tutorialzine.com/2010/06/making-first-chrome-extension/ -Finally, a basic tutorial covering the steps to creating an extension.

With these resources you should be well on your way to building your first Chrome extension as well. I was surprised how much of it was simply Javascript, and if your comfortable with that, you'll have no problem learning extensions. Have any other good resources? How about a browser extension that you shouldn't live without? Share it in the comments!

-Jonny

More Entries