Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Saturday, 18 June 2016

Why just grabbing code from the web can lead to major problems down the line

Why just grabbing code from the web can lead to major problems down the line

By Strictly-Software.com

I have wrote many articles over the years about server, system, website and PC performance, and it seems that the more versions of FireFox and Chrome that come out, the slower they get. I don't think I have ever used IE 11 as much as I have in the last 3 months. Mostly just to get Facebook, Radio 1 or Google+ to load within a minute which FF and Chrome seem to have issues with for some reason.

Some add-ons like uBlock Origin prevent 3rd party domain code from being loaded up on the site as well as large image or video/flash objects. It also stops pop-up windows and the loading of remote CSS fonts which is all the craze now.

What the developers of these websites don't seem to realise is that when they are loading in code from all over the web just to make a page display or run it causes a lot of network traffic. It also introduces the possibility that the code at the end source has been tampered with and therefore you could be loading in Cross Site Scripting hacks or ways for people to exploit your site if that certain script exists in the DOM.

Also a less likely scenario but a more common issue is that the more domains your site has to access to get all it's code onto the site, it can mean the page doesn't load as you may want it to, or even not at all.

If script A relies on Script B but Script B doesn't load for a long time then the code in Script A that was going to open a popup window on DOM Load, or play a video just isn't going to work.

I recently overrode the Window.OnError event and logged the Message, URL and Line No with an AJAX call to a log file before either throwing the error for modern sites or hiding it for older ones.

When I started looking through these files the amount of Google AdSense and Tracker scripts not loading due to timeouts is incredible. Also there are issues with bugs in the scripts or due to their slow loading objects not being available for other scripts relying on them to use. An example of just one error is:

24/04/2016 09:54:33 : 8X.XXX.XXX.161 'document.body' is null or not an object in http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js on line 19

People relying on Google for stats shouldn't for a number of reasons. Not only do they not always load and record the visit, but they also rely on 3rd party cookies being enabled and JavaScript being enabled. A Log parser or DB is a much better way to log every single visitor BOT or Human.

For example if you have a main jQuery script you are loading in from a CDN or from a site you don't control, if that domain is having network problems then that means any other code on the site reliant on it won't be able to work until that issue is resolved. This happens a lot from viewing the messages in my JavaScript error log file.

Due to this a lot of  people just grab the code off the net and load it in from a local server to get round network delays.

However by doing this they are stuck in a point of time (the date and the version they copied the file at). I hate this, as instead of actually learning JavaScript so they know what they are doing they are relying on some other blokes framework to solve their problems e.g have a look at whose code most of you are building your site with. If there is a bug in jQuery you either have to fix it yourself or wait for John to fix it. If it's your own code at least you can rely on your own skills and know how the code works.

The other day I had to solve a jQuery problem where the page in question was using an old version of jQuery and another 3rd party script built around jQuery (but not by John), called reveal.js.

As the front end developers wanted to move to the latest version of jQuery they suddenly found that the reveal.js code no longer worked.

After debugging it was clear that the $().live(function) had been removed and as the code that did the popup relied on reveal.js and it was built in 2011 with no recent updates. The whole revealing and hiding of modal boxes stopped as soon as a modern version of jQuery was loaded in for the site.

I had to waste time reading up on jQuery and then hardcoding the version of reveal.js as we had to use the new .on() function so that the new jQuery libraries would work with the old code that was taken from a library developed in 2011.

This is one thing I hate about front end developers who just pick n choose libraries off the web despite them all doing the same thing like event binding and removal multiple times in multiple ways.

If they are relying on a 3rd party library they took from 2011 that also relies on a constantly updated framework like jQuery that is always dropping and adding new methods, then how are people to expect sites to work when a method these libraries rely on are removed?

If they cannot write some basic notes to say that this page relies on this script e.g reveal.js, which came with jQuery 1.4.5 then it makes people like me who hate debugging other peoples frameworks hate 3rd party code even more.

Not only do I have my own getme.js framework which is simple, uses CSS selectors, linked methods where the array of objects is passed down from function to function, but now that most browsers support the simple one line of code that allows for selectors to find objects there is no need to add Sizzle.js to it any-more. Not unless you really want to support old IE versions you can just use this single line.

// where query is the CSS selector
document.querySelectorAll( query ); 

For example in my Getme.js code this following line of code will loop through all Anchor nodes with a class of menu on them inside the DIV with the ID MAIN. I just then alert out the elements ID.

G('DIV#Main > A.menu').each(function(){
   alert(this.id);
})

Obviously if you do all your styling in CSS or inline JS you have the option of how to style a series of objects for example with the .setAtts method you can pass in any element attribute and their values.

This is providing a mixture of a class and inline styles to the Paragraphs inside DIV tags. It also uses chaining where the array of object are passed from one function to the next just like other frameworks.

The first example just looks for DIV tags with P's inside and sets the class to "warningRed" and the style of the font to bold and red. The class can do most of the styling or ALL of it.

It's just an example, so is the 2nd one that shows all P tags with a SPAN with the class "info". Inside it gets a warning message with the .setHTML method and then the .setStyle method colours the text.


G('DIV > P').setAtts({class:"warningRed", style:"color:red; font-weight:bold"});

G('P > SPAN.info').setHTML('CLick for help.').setStyle({color:red, fontSize:8px});


I used a G instead of $ just to distinguish it from all the other frameworks and because it's called Getme.js.

If you want to know how to learn to write your own chainable framework then have a read of this article of mine. I've kept Getme.js simple as I hate people who just copy code from the web especially when it goes wrong.

At least this way I have a wrapper object that allows for chaining and the setting of multiple attributes at once and the use of selectors. However I still like to use pure JavaScript inside my functions so people down the line can get their heads around it.

So next time I get a jQuery problem because John Resig has decided to remove a core function from his framework which then causes a chain re-action due to all the other frameworks that were built around that version of jQuery, I can at least (hopefully) use my simple framework to apply the CSS that the designers need to rather than spend a day hunting around for fixes to other people's code.

That, is something I really hate doing.



By Strictly-Software.com 

© 2016 Strictly-Software.com

Thursday, 22 August 2013

Handle jQuery requests when you want to reference code that hasn't loaded yet

Handle jQuery requests when you want to reference code that hasn't loaded yet

As you should be aware it is best practise to load your JavaScripts at the bottom of your HTML for performance and to stop blocking or slow load times.

However sometimes you may want to reference an object that is not yet loaded higher up in the page.

If you are using a CMS or code that you cannot change then you may not be able to add your event handlers below any scripts that maybe needed to use them. This can cause errors such as:

Uncaught ReferenceError: $ is not defined 
Uncaught ReferenceError: jQuery is not defined

If you cannot move your code below where the script is loaded then you can make use of a little PageLoader function that you can pass any functions to and which will hold them until jQuery (or any other object) is loaded before running the functions.

A simple implementation of this would involve a setTimeout call that constantly polls the check function until your script has loaded.

For example:


PageLoader = { 
 
 // holds the callback function to run once jQuery has loaded if you are loading jQuery in the footer and your code is above
 jQueryOnLoad : function(){}, 

 // call this function with your onload function as the parameter
 CheckJQuery : function(func){
  var f = false;

  // has jQuery loaded yet?
  if(window.jQuery){
   f=true;
  }
  // if not we store the function if first time in loop otherwise and set a timeout
  if(!f){
   // if we have a function store it until jQuery has loaded
   if(typeof(func)=="function"){    
    PageLoader.jQueryOnLoad = func;
   }
   // keep looping until jQuery is in the DOM
   setTimeout(PageLoader.CheckJQuery,200);
  }else{
   // jQuery has loaded so call the function
   PageLoader.jQueryOnLoad.call();    
  }
 }
}


As you can see the object just holds the function passed to it in memory until jQuery has been loaded in the DOM. This will be apparent because window.jQuery will be true.

If the object isn't in the DOM yet then it just uses a setTimeout call to poll the function until it has loaded.

You could increase the length of time between the polls or even have a maximum limit so that it doesn't poll for ever and instead after 10 loops returns an error to the console. However this is just a simple example.

You would call the function by passing your jQuery referencing function to the CheckJQuery function like so.


<script>
PageLoader.CheckJQuery(function(){
 $("#myelement").bind('click', function(e) { 
  // some code
  alert("hello");
 });
});
</script>


It's just a simple way to overcome a common problem where you cannot move your code about due to system limitations but require access to an object that will be loaded later on.

Monday, 20 May 2013

Some clever code for SEO that won't annoy your users

Highlighting words for SEO, turning them off for the users

You might notice in the right side bar I have two options under the settings tab "Un-Bold" and "Re-Bold".

If you try them out you will see what the options do. Basically unbolding any STRONG or BOLD tags or re-bolding them again.

The reason is simple. Bolding important words either in STRONG or BOLD tags is good for SEO. Having content in H1 - H6 tags are even better and so are links - especially if they go to relevant and related content.

However, I don't claim to be the first person to start bolding important keywords and long tail sentences for SEO purposes but I was one of the first to catch on that the benefits for SEO were great.

To much bolding and it looks like spam, too little you might not get much benefit but you have to 2 areas to cater for.

1. The SERP crawlers (Googlebot, BingBot, Yandex etc etc) who see the original source code on the page. When they do they will just see words wrapped in normal STRONG and BOLD tags (See for yourself).

2. However if a user doesn't like the format and mix of bolded and non bolded wording then they can use the settings to add a class to all STRONG and BOLD tags that basically takes aways the font-weight of the element. You would only see this in the generated source code. Running the "Re-Bold" function after the first "Un-Bold" will just remove the class that took away the font-weight in the first place returning the element to it's normal bolded state.

Therefore the code is aimed for both BOTS and users and you can see a simple test page on my main site here: example to unbold and rebold with jQuery.

I have used jQuery for this only because it was simple to write however it wouldn't be too hard to rewrite with plain old JavaScript.

Another extension I have lost since updating this blog format but would be easy to add is the use of a JavaScript created cookie to store the users last preference so that they don't have to keep clicking the "un-bold" option when they visit the site.

As Blogger won't let  you add server side code to the blog you will need to do it all with JavaScript but with the new blogger layout (which I love by the way - unlike Google+) it is easy to add JavaScript (external and internal) plus CSS sections and link blocks to control the actions of your functions.

An example of the code is below and hopefully you can see how easy it is to use.

First I load in the latest version of jQuery from Google.

Then I use selectors to ensure I am only targeting the main content part of the page before I add or remove classes to STRONG or BOLD tags.

<style type="text/css">
.unbold{
 font-weight:normal;
}
</style>

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>

<script>
function unbold()
{
 $(".entry-content").each(function(){  
  $("strong",this).addClass("unbold");
  $("b",this).addClass("unbold");
 });
}

function bold()
{
 $(".entry-content").each(function(){
  $("strong",this).removeClass("unbold");
  $("b",this).removeClass("unbold");
 });
}
</script>

So not only are you benefiting from SEO tweaks but you are letting your users turn it off if they feel it's a bit too much. Hey Presto!

Saturday, 18 May 2013

Why I hate the new Google+ API

I absolutely hate the new Google+ API

Yes Google+ have had a revamp and if you are not on it then you won't know what the old version was like if you now join.

To me it's as if someone has read too many books on the jQuery effects library and basically orgasmed code across the API.

If you go to type a new status message into a box the whole page shifts round so that your box moves to the centre of the screen and the rest of the messages and segments of the page do a little jig around it so that you are supposed to go "wow".

Not me. Too much API Jizz is something I hate. 

Not only does it repeatedly turn my PC into a helicopter as the CPU rises and falls like a coke head on the lash but it just is too much for my ageing eyes.

It really seems to me as if someone is showing off by writing their "funky" API code. Hey boss look what I can do with a shit load of JavaScript that takes ages for all the page segments to load but makes non techies go "oooh" as they see it in action.

Whilst an API should be friendly and easy to use there is nothing "useful" about the whole screen moving around just so your current type box is in the middle of the screen.

Why not just put the "new message" box in the middle to start with?

Not only that but the amount of times I go to reply to a conversation down the right hand side and someone I have never seen before pops up in a box on top of the place I am trying to write is beyond annoying.

It means not only can I hit the send button but sometimes if I can find a way to get rid of the annoying box (and that's not 100% of the time) the message I was writing disappears!

I know writing the whole page in JavaScript stops (or limits script kiddy's) from scraping easily but there really is a limit. Personally I just think Google+ have crossed it and that there was nothing too wrong with their old API.

What do you think?


Tuesday, 12 June 2012

New unobtrusive EU Cookie Compliance Code

Some people have said that my JavaScript lightbox method for EU Cookie compliance is a bit off putting for users as it takes them away from the site if they disagree.


Also at the last minute the EU has allowed compliance with their new cookie privacy rules through a much easier method.

This allows sites to show the users that cookies are being used with a link to the sites cookie policy and a button to hide the message in future. If they continue to use the site they have basically agreed to the use of cookies.

You may have noticed on sites like the BBC or Channel 4 etc they will show a little piece of text at the top of the page that slides out and tells the user that cookies are being used with a link to their cookie policy. They also have a button to set a cookie so the message isn't shown again.

This method is a lot less intrusive and doesn't take the user away from the site as there is no "agree" or "disagree" button.

Therefore for those of you not wanting to make use of my EU Cookie compliance code I have created a newer version that follows this new format which can be seen here.

It is up to you to create a cookie policy document that details how cookies can be viewed and disabled through various browsers and toolbars etc. Plus the text and styling is up to you but the example html page shows this new unobtrusive method in action.

The code makes use of jQuery to handle the animations so load in the source code from your usual repository e.g Google or jQuery e.g: http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js
  
The actual JavaScript for the html test page is below.


// run immediatley - place in footer
(function(){

 EUCookie = {
  
  confirmed : false,

  Confirm : function(e){   
   var self = this;
   
   // create cookie   
   self.CreateCookie("EUCookie",1,365);  
   
   // slide back in the cookie bar
   $("#cookieWarning").animate({
     height: 0
   }, 300, function(){
    $("#cookieWarning").css("display", "none");
   });
   
   return false;
  },

  CheckEUCookie : function(){

   var self = this,
    val = self.ReadCookie("EUCookie");
   
   // if our cookie has been set
   if(typeof(val)!=undefined && val==1){   
    self.confirmed = true;
   }
 
   return self.confirmed;
  },

  CreateCookie : function(name,value,days) {
 
   if (days){
    var date = new Date();
    date.setTime(date.getTime()+(days*24*60*60*1000));
    var expires = "; expires="+date.toGMTString();
   }else{
    var expires = "";
   }
   
   document.cookie = name+"="+escape(value)+expires+"; path=/";
  },

  ReadCookie : function(name){
   
   var nameEQ = name + "=";
   var ca = document.cookie.split(';');
   for(var i=0;i < ca.length;i++) {
    var c = ca[i];
    while (c.charAt(0)==' ') c = c.substring(1,c.length);
    if (c.indexOf(nameEQ) == 0){
     var r = unescape(c.substring(nameEQ.length,c.length));    
     return r;
    }
   }
   return null;
  }
 };

 // add click event;
 $("#cookie-close-button").click(function(){EUCookie.Confirm()});
 
 // if no cookie set show form
 if(EUCookie.CheckEUCookie()){
  
  // cookie already set so hide box
  $("#cookieWarning").css("display", "none");
 }else{
  
  // Show the form - set to zilch then slide out to the appropriate height
  document.getElementById("cookieWarning").style.display = "none";
  document.getElementById("cookieWarning").style.height = 0;
  
  $("#cookieWarning").animate({
     height: 28
   }, 500, function(){
  }).css("display", "block");    
 }
})();

To view the code in action you can visit the demo page and download the html page and tweak it to your hearts desire.

Let me know what you think.

Saturday, 2 June 2012

The Strictly iPhone Console - Debugging on iPhones

The Strictly iPhone Console - Increasing real debugging without simulators



Yesterday I blogged about debugging on iPhones and how useful the Debugger console was which showed up JavaScript errors and console messages without the need for an agent switcher. However I complained about the lack of ability to view the generated source code on the page when viewing on an iPhone.

However I remembered sometime back I blogged about some bookmarklets I was using that enabled me to view the generated source on older browsers like IE 6 and tonight I knocked together a little script that enables you to have basic "onscreen" debugging functionality when using an iPhone.

The code is some basic JavaScript that adds a DIV area to the bottom of the current page with two links at the top that allow you to view the generated and raw source code for the page. These open up in new windows and  if you have problems with them opening you might need to enable the popup window functionality in your iPhone Safari settings first.

Underneath is a basic console which due to the lack of scrollbar functionality on DIVs with overflow:auto or overflow:scroll I have created with a readonly textarea. If you need to scroll down the console you should use the two finger drag option to move the content within the textarea up or down.

I have also overwritten the window.console object so that the console.log function pipes out messages to this console if it's being used.

To Install the Strictly iPhone Console

1. On your iPhone visit this page and copy the JavaScript code from the area below into your clipboard. or vis

2 .Bookmark any page on your phone and then go into your bookmarks and edit it. Change the name to "Strictly Console" before pasting in the copied source code as the URL location for the bookmark.

3. Test that the code is working by going to a webpage on your iPhone and once the page has loaded open your bookmarks and select the "Strictly Console" bookmark. The console should appear at the bottom of the page. At the top of the console will be two links in a grey background "View Source" and "View Generated Source". Underneath will be the console area.

Clicking on either of those links will open up a new page with either the original source code or the generated source code from the current page. If nothing happens when you click the link check your Safari settings so that you allow Pop Ups. When you click the link it will ask you whether you want to open the pop up or not. Choosing Yes will show you the source in a new window.

4. To pipe debug out to the Strictly iPhone console window just use the standard console.log('hello'); function to do so.

As with all code that relies on DOM element make sure you check for the the existence of the console before trying to access it e.g a simple debug function could look like this:


function ShowDebug(m){
 if(typeof(window.console)!="undefined")){
  console.log(m);
 }
}


If you have problems copying and pasting from here (due to the crumy formatting of HTML in blogger) then you can download the compressed script from this location: iphoneconsolebookmark.js

javascript:(function()%7B
function%20htmlEscape(s)%7Bs=s.replace(/&/g,'&amp;');s=s.replace(/%3E/g,'&gt;');s=s.replace(/%3C/g,'&lt;');return%20s%7D
var%20_d=document,jq=_d.createElement('script');jq.type='text/javascript';jq.async=true;jq.src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js';var%20s=_d.getElementsByTagName('script')[0];s.parentNode.insertBefore(jq,s);
console=%7B
log:function(m)%7var%20l=_d.getElementById("logwindow"),txt=l.value,nt=((txt==""||/^\s+$/.test(txt))?m:txt+m)+"\n";l.value=nt;%7D,
GenSource:function()%7Bconsole.ViewSource(htmlEscape('%3Chtml%3E\n'+_d.documentElement.innerHTML+'\n%3C/html%3E'))%7D,
RawSource:function()%7B$.get(location.href,function(data)%7Bconsole.ViewSource(htmlEscape(data))%7D)%7D,
ViewSource:function(s)%7Bx=window.open();x.document.write('%3Cpre%3E'+s+'\n%3C/pre%3E');x.document.close()%7D%7D;
var%20log=_d.createElement("div");log.setAttribute("id","logger");log.style.visibility="visible";log.style.display="block";log.style.zIndex="2147483647";log.style.position="relative";log.style.backgroundColor="#fff";log.style.border="1px%20solid";log.style.width="98%";log.style.left="0";log.style.bottom="0";log.style.height="3300px";log.style.padding="5px";log.style.textAlign="left";_d.getElementsByTagName("body")[0].appendChild(log);var%20link=_d.createElement("div");link.style.width="100%";link.style.background="lightgray";link.style.color="navy";link.style.fontWeight="bold";link.innerHTML="%3Ca%20onmouseout='this.style.color=\"navy\";'%20onmouseover='this.style.color=\"blue\";'%20style='text-decoration:none;'%20href='#'%20onclick='console.RawSource();return%20false;'%3EView%20Source%3C/a%3E%20|%20%3Ca%20onmouseout='this.style.color=\"navy\";'%20onmouseover='this.style.color=\"blue\";'%20style='text-decoration:none;'%20href='#'%20onclick='console.GenSource();return%20false;'%3EView%20Generated%20Source%3C/a%3E";log.appendChild(link);var txt=_d.createElement("textarea");txt.setAttribute("id","logwindow");txt.setAttribute("readonly","readonly");txt.style.width="100%";txt.style.height="300px";log.appendChild(txt);
%7D)();


Test the Strictly Debug Console now

You can try the Strictly iPhone debug console out on this page by clicking the following button which will add a new debug message to the console on each click. The console should already have 15 messages inside it which were added when the page loaded. Remember the console will be at the very bottom of the screen so scroll right down to see it.



Obviously this is a very basic iPhone Console and nothing like Firebug or Chromes inbuilt console but it could be easily expanded with a little work and by loading in the jQuery iphone library you could easily create a popup DOM inspector that was initialised by a long tap down event to show the current elements styling and positioning. Let me know what you think and if you amend it to add more features let me know so I can update the code here.

To read why a proper debugging console ON the device is required rather than a user-agent switcher then read this article I wrote about debugging on iPhones.

Saturday, 24 March 2012

Logging and Suppressing JavaScript errors

Logging JavaScript errors to a file by overwriting the window.onerror method

Sometimes you may have intermittent JavaScript errors that you cannot re-produce or maybe you just want to be able to log JavaScript errors for later viewing. Or maybe you just want to suppress them so that end users don't see them.

By using the useful and also dangerous feature of being able to overwrite core JavaScript functions and objects you can utilise this to your advantage by overwriting the window.onerror method.

The window.onerror method takes 3 parameters which are:

  • message : the error message
  • url: the URL of the file that raised the error message
  • line: the line number that the error occurred on.


Therefore it is very easy to create your own window.onerror function to take these values and then make an AJAX call to a server side page which logs the JavaScript error details to a file or database or even sends an email.

Also by overwriting the window.onerror function we can suppress JavaScript errors if we chose to.

Maybe if we are debugging a script and don't want the error console constantly filled up or maybe some of our users are still using Windows 98 and use IE 4.

If we are using unobtrusive JavaScript that builds layer upon layer of functionality starting with the lowest common denominator e.g HTML, then adding JavaScript functionality if they have it, Flash if they have it and so on then we may want to just suppress these JavaScript errors in old browsers.

To suppress a JavaScript error you just need to return true.

This example uses jQuery seeing that is so popular but any AJAX library can be used. The point is that you are taking the error parameters and logging them somewhere useful.

I have used a little JavaScript wrapper object to set some system properties like a config object to define whether error logging is on or off and whether or not to suppress errors in older browsers. 

The code to define which browsers to suppress in can be left to you but I have done a simple test for document.getElementById which means browsers like IE 4 and NN4 won't get errors raised.


// Log JS errors to a file - file is overwritten each day only use when debugging a particular page/site

// Set up our global config options to decide whether to log JavaScript errors and whether or not to suppress them. A simple false/true could suffice but we might want to test for old browsers or certain features. If the browser doesn't support document.getElementById it's a pretty old browser!
GlobalSettings = {
 SystemName : "Strictly-Software",
 Version : 2.0.1,
 LogJSErrors : true,
 SuppressJSErrors : (document.getElementById) ? false : true
}

// override the onerror object
window.onerror = function(msg, url, line)
{   
 // does our global system want to log errors - this could be a Client or Serverside setting
 if (GlobalSettings.LogJSErrors)
 {  
                // using JQuery to post a GET request to a page that logs the error details
  $.get("logJSError.php", { message: msg, errorlocation: url, lineno: line } );
 }
 

 // do we still raise the error or for old browsers which might have a lot of errors do we try and supresss them? Use our global config options again.
 if(GlobalSettings.SuppressJSErrors){  
  // return true to suppress the error so its not raised to the console.
  return true;
 }else{
  // return false to raise the error to the console.
  return false;
 } 
}

Then all you need is to define your server side page logJSError.php (or whatever language you are using) to collect the error data from the request and do whatever you want with it e.g log it somewhere for later viewing.

Remember whilst being able to overwrite functions that already exist is good in certain situations like this and the Lazy Function scenario but it can also cause you severe debugging nightmares like the one I discovered when using the common addEvent naming convention for cross browser adding of events.

Therefore be careful especially when overwriting core JavaScript features but also use them to your advantage when possible.


Sunday, 18 October 2009

Using Document onLoad instead of Window onLoad

Why we should use DOMReady instead of window.onload

I know this is a topic that has been extensively covered by many developers but I thought I would give an example of a good reason to use a function that tests for the DOM to be ready to start running your JavaScript functions instead of the window.onload event to fire.

JQuery users will be used to writing code like this:
$(document).ready(function() {
alert("DOM is ready")
});
Which will try to fire when the DOM is ready and if not when the window is ready. If you write code like this:
$(window).load(function() {
alert("Window and all Iframe and Image content is ready")
});
Then you are waiting for the window to be ready to fire your functions which maybe a safer option if you are wanting to manipulate image or iframe content or content that has been loaded externally but on most occasions you would want to use a onDOMReady function like the one below which I have taken from code I use on this site. It is pretty similar to how most major frameworks including JQuery have inside their own cross browser DOMReady functions, however I am still testing for old Opera and Webkit users whereas JQuery does not do this anymore.

Also I have had to take the compressed version and then unpacked it due to not being on my own PC tonight due to problems with my leg but the code shouldn't be too bad and shouldn't be treated as copy and paste code but more like pseudo code.
onDOMLoad: function () {
// only look for modern browsers that haven't spoofed their agent (not perfect check but then its their own fault!)
if (Browser.w3cDOM && !Browser.spoof) {
// For old webkit and opera and KHTML we use a timer to test for readystate
if ((Browser.webkit && Browser.webkitversion < 525) || Browser.khtml || (Browser.opera && Browser.version < 9) && document.readyState != "undefined") {
PageLoader.DOMTimer = setInterval(function () {
if (/loaded|complete/.test(document.readyState)) {
PageLoader.RunDOMLoadFunctions()
}
},
10)
// For other standard compliant modern browsers apart from IE we can use the standard DOMContentLoaded function
// making sure to remove the anonymous function straight away.
} else if (document.addEventListener) {
AddEvent(document, "DOMContentLoaded", function () {
RemoveEvent(document, "DOMContentLoaded", arguments.callee);
PageLoader.RunDOMLoadFunctions()
},
false)
// For IE (and Opera which is why we do this last) we try two methods the first one can sometimes
// fire very late on in the day.
} else if (document.attachEvent) {
AddEvent(document, "onreadystatechange", function () {
if (document.readyState === "complete") {
RemoveEvent(document, "onreadystatechange", arguments.callee);
PageLoader.RunDOMLoadFunctions()
}
},
false);

// We also do this trick by Diego Perini to continually check for DOM readiness by
// checking for an error which comes from the doScroll call. Once there is no error reported we
// know the DOM is ready. This doesn't work for Iframes note the window comparison.
if (document.documentElement.doScroll && window == window.top)(function () {
if (PageLoader.DOMLoaded) return;
try {
document.documentElement.doScroll("left")
} catch(e) {
setTimeout(arguments.callee, 0);
return
};
PageLoader.RunDOMLoadFunctions()
})()
}
};
// Always call a window onload function to run anything not fired already.
PageLoader.AddWindowLoadEvent(function () {
PageLoader.RunDOMLoadFunctions()
});
return true
}
}


A good reason for doing this came to light very recently with a system we are working on updating the folder structure that holds site related files such as images, banners, logos etc that are loaded and used on the site. Because we are halfway through the process the folder structure has changed which means when a page loads all the images appear broken as they haven't been moved to the new structure yet. This is obviously on a development server!

However on a few pages it has been noticeable that as the page is loading you may start filling in form fields for input and then suddenly halfway through your focus is removed from the field you are currently on and taken to another field, usually the first field on the page. The reason for this is that the code being used to run the set focus() is being called by a function that is using a window.onload event rather than an onDOMReady function.

This doesn't happen in all places and I haven't fully looked into it to see if its mainly an Internet Explorer problem, which has well known issues with firing functions between the loading of the DOM and the Window, or an issue where the DOMReady doesn't fire for whatever reason and then falls back to window.onload. However its an example of the problem that when using window.onload the browser will wait until all images have been loaded before firing and due to the number of broken banner logos and images this is quite a number of failed checks which is why the delay seems so long.

A quick thought has been to add an extra fallback call in between the DOMReady and window.onload which would fire once the Body has loaded. This could be similar to a function below of mine which polls the DOM looking for the Body element to be ready before running the desired function. Again this is taken directly from unpacked code with comments re-added so treat as pseudo code.
onBodyLoad: function (fn) {
// Check the function is a function e.g typeof(fn)=="function"
if (S.isFunction(fn)) {
// if we haven't already called this function
if (!this.Body[fn]) {
// if the body has already loaded and we can get a reference to it
if (P.BodyLoaded || S.getBody()) {
// Call the function
fn.call();

// Set some flags so we know we have called the functon and also
// that the body has loaded in case other functions (window/dom) ready haven't
P.Body[fn] = true;
P.BodyLoaded = true
}
} else {
// call a timeout passing in the function to check again in 50ms
setTimeout(function () {
P.onBodyLoad(fn)
},
50)
}
}
}


Depending on what you put between the close BODY tag and the close HTML tag which shouldn't be much this might help give the effect of a DOMReady if for whatever reason the original code doesn't fire.

Sunday, 4 October 2009

Build your own framework

Creating your own framework

Lets build our own mini framework. Not to replace the many brilliant frameworks already out there but to educate ourselves in how we would go about such a task. Many of you maybe using JQuery, Prototype. YUI or MooTools but have no idea how that code actually works and looking at the source code may just put you off wanting to know. Therefore this article is going to show you how we can create our own basic framework which we could extend and use ourselves if we so wished or we could build it just for fun as a way to improve our JavaScript programming knowledge as well as helping us understand why frameworks are built the way they are.

If you want to jump straight in and have a look at the full source code first then you can download the latest release of the code from here: http://www.strictly-software.com/scripts/downloads/getme-1.0.4.js


Aims of our example framework.

1. Picking an eye catching name

All the current major frameworks have great names that are easy to remember such as JQuery, Dojo and MooTools.

I am going to call this framework Getme for the simple reason it Gets me what I want. I already use a function in a lot of my code including Strictly.js called getEl which has a short name of G so Getme seems like a logical progression.

Also I am currently in the process of building an AJAX based add-on that is based on accessing remote content and reformatting it for use on websites in various ways so the name Getme.js seems to be in keeping with the aims of this add-on which will no doubt implement this code or a variation of it when complete.

One of the things I tend to do when creating a codebase or function libaray is give all my important functions two names. The first name is the more descriptive one such as Getme and the second one is usually a single capital letter which I will use to reference the function once the code has been compressed with my compressor tool e.g

G = getEl = function(id){ return document.getElementById(id); }

2. Main Functionality

The codebase is a very cut down version of a framework designed to show how you would go about creating your own as well as helping us understand what's going on when we look through the source code of other popular frameworks therefore the functionality will be very minimal. However it would be nice if we could do the following:

1. Return DOM nodes in a variety of manners using the same function. Similar to how JQuery lets you pass in CSS-Selectors to the $ function.

My function will do without the onDOMLoad function that JQuery supports with its main constructor when an anonymous function is passed in but it will offer the ability to return nodes in a variety of ways including by passing the constructor an #ID, .className, <tag>, a P:first-child > SPAN CSS Selector or an HTML string which can be converted into a node list.

2. Enable chaining so that the result of a previous DOM manipulation can be passed to subsequent functions without having to pass the previous functions result as a parameter into the next function e.g

// instead of having to do this
Getme.setHTML(Getme('DIV > P.myClass'),"<p>New HTML</p>");

//we could do this
Getme('DIV > P.myClass').setHTML("<p>New HTML</p>");

Which would set the innerHTML of any Paragraphs with a className of myClass that are descendants of DIV tags to have the value <New HTML>

3. Make use of the CSS 3 Selectors that all the major frameworks use to return DOM elements e.g

DIV#myID > P.myClass:nth-child(3)

4. Make use of internal loops so that a method applied to the result of a selector acts on all the nodes returned for example:

// set the class to "blue" and set some style properties for all elements that match the selector e.g all P tags that are descendants of DIV's
Getme('DIV > P').setAtts({class:"blue",style:"color:red;font-weight:bold"});

5. The ability to extend our object very easily by passing another object to a method that will add any properties and functions from one object to the other.

That's all I want this example code to do which if you think about it is pretty powerful stuff anyway. The code format is loosely based on JQuery and if you have ever looked at JQueries source code and wondered what the hell is going on then this example might help you get your head round it.


Starting to build our Getme.js framework - The Wrapper function

Now you may have seen in JQuery and other frameworks or functions that the entire code is wrapped in a self calling anonymous function like so:

(function(){

// set some shortcuts to optimise references to global vars
var window = this, // pointing window to this (which refers to the global object) speeds up references to window

undefined, // this creates an undefined variable we can use to test other undefined variables against

query = !!document.querySelectorAll;

// rest of code e.g object definition, functions and methods
})()

Now this self calling anonymous function ensures that any objects defined within it are created and returned when the function runs. The open and close () brackets at the end of the anonymous function ensure this. Plus any variables declared within the function are only accessible inside the anonymous function which means we are not polluting the global namespace. To learn more about this modular method please read the following article:

http://yuiblog.com/blog/2007/06/12/module-pattern/

As you can see there are a few variables declared within this function that to the beginner may see pretty odd e.g
var window = this,

undefined,
How can you set window to be equal to this?

Well the this keyword is a special keyword that refers to the context object and defaults to the global context (window) when used outside an object. If I create an instance of my Getme object and use the this keyword this inside a Getme method then it refers to the current instance of the Getme object. Used outside any object it will refer to the window object.

If you don't know much about the this keyword I suggest reading up about it as there is a well known problem in Internet Explorer where the this keyword refers to the Window object in certain cases when it shouldn't such as when event listeners are added using IE's attachEvent function. A recent blog of mine detailed one such problem that explains this issue in detail: http://blog.strictly-software.com/2009/09/trouble-with-this-keyword-and.html

If you define a function outside a namespace then you would also be able to reference it by prefixing it with window e.g a function called myFunc could be called as window.myFunc. Outside objects and functions where scope has been passed with a call or apply method the this keyword refers to the global namespace therefore setting window = this is a way of accessing the global namespace quicker than referring to it through the keyword window.

The other variable that is declared but not set to anything is undefined. Because the variable hasn't had a value set it's basically undefined and this means any comparison tests with other variables can be speeded up by comparing the variable with this undefined variable rather than having to do an equality test such as:
var myvar;
if(myvar == "undefined") alert("undefined");
if(myvar === undefined) alert("undefined");
The first test has to convert myVar to a string whereas the second test compares one undefined value against the other which is much quicker.


Now to the Getme.js source code

As most DOM manipulation involves the returning of one or more nodes from the DOM the aim of this object is to enable quick retrieval of DOM nodes by various methods. The main object will be called Getme and its primary property will be an array called this.nodes which will hold the current set of nodes. For functions that only return one node element such as getElementById then this node array will contain one item located at the first index e.g

this.nodes[0];

To enable the chaining of our object methods together each method will return a reference to the Getme object after any DOM manipulation has been carried out.

Lets add some code to instantiate our Getme object which we will use as our primary method of returning DOM nodes. We will also give it a short cut name of G (for Getme) so we can access DOM elements in a similar way that JQuery and the others use $ instead of jQuery.

// the G function is a short cut to instantiating with Getme object
G = function(sel, context){

return new Getme(sel, context);
}

// the Getme constuctor pass in a selector/context
// can handle multiple forms of selector
// ID - #ID to return element by id e.g G('#myID') 
// CLASS - .myClass to return elements by className
// TAG - <P> or <SPAN> to return all elements of a certain node type
// SELECTOR - DIV P SPAN to return all elements that are SPANS decendants of P who are decendants of DIV
// NodeList - <DIV><SPAN>hello there</SPAN></DIV> will return a nodelist containing the elements specified by the HTML passed in
Getme = function(sel, context){  

this.Getme = "Getme version 1.0.4",

this.nodes = this.nodes || [],

this.context = this.context || document; // default context to document

// main regEx to determine if selector is HTML string, ID or classname
var re_getme = /^<([^> ]+)[^>]*>(?:.|\n)+?<\/\1>$|^(\#([-\w]+)|\.(\w[-\w]+))$/i,

// regEx to match <SPAN> <P> <H1> tags
re_tag = /^<([a-z1-9]+?)>$/i,   

match;

// if no selector passed in default to document
if(!sel) sel = document;

// if we have been passed a string then this could either be
// an id of an element, a class name, a tag or an HTML string
if(typeof(sel)==="string"){

// look for a single HTML tag e.g <P> or <SPAN>
match = re_tag.exec(sel);    

if(match && match[1]){

// return all nodes matching the tag      
this.nodes = this.context.getElementsByTagName(match[1]);
}else{

// run regex to look for ID, class or HTML string
// match[1] = HTML string - matching start and end tag
// match[3] = ID
// match[4] = class name
var match = re_getme.exec(sel)||[];

// if we have an ID with or without a # e.g #myid or myid treated as an ID
if(match[3]){     

// get element by id
this.nodes[0] = document.getElementById(match[3]);

// if we have a class name e.g .myClass
}else if(match[4]){

// get elements by class name
this.nodes = Getme.funcs.getElementsByClassName( match[4],this.context);

// if we have a valid HTML string e.g <P><STRONG>hello</STRONG></P>
}else if(match[1]){

// create nodes from html
var div = document.createElement("DIV");

div.innerHTML = sel;

this.nodes = div.childNodes; 

}else{

// if querySelectorAll is available for modern browsers we can use that e.g
// FF 3.2+, Safari 3.2+, Opera 10, Chrome 3, IE 8 (standards mode)
if(query && this.context === document){       
this.nodes = Getme.funcs.selector(sel);
}else{            
// otherwise revert to Sizzle which makes a good job of handling older browsers
this.nodes = Getme.find(sel,this.context);
}

}
}
}else if(sel.nodeType){

// already got a node add
this.nodes[0] = sel;

}else if(Getme.funcs.isArray(sel)){    

this.nodes = sel;

}else{

this.nodes = Getme.funcs.makeArray(sel);
}

this.length = this.nodes.length;

return this; 
}

This function Getme which can be called with the shortcut G is the key to the whole object. It accepts two parameters sel and context. Sel (which is short for selector) can be a number of parameter types such as:
  • #myID which will return a reference to an element with the ID myID
  • .myClass which will return a node list of all elements with the className myClass
  • <span> or <p> which will return a node list of all elements with the tag specified e.g SPAN or P
  • <div><span>hello</span></div> which will be used to create a node list from the HTML specified.
  • An array of elements can be passed in gained from some other method or an object hash of elements which will be converted into an array.
  • DIV#myID > P:first-child or any other CSS 3 Selector can be passed in and a nodelist will be returned if the browser in question supports document.querySelectorAll. Otherwise if Sizzle has been added to the codebase then it will handle older browsers without the use of XPath.
Also notice the internal properties this.context and this.nodes as these are very important in terms of storing the current context that any selections are being carried out against such as document or a particular node. The nodes property stores the current nodelist that any selector or function has retrieved. For example if you have just returned a list of P tags then the nodes array will hold references to these items. This allows subsequent methods to access the nodelist to do any work such as set styles, clear children etc.

The Getme function uses various different methods to return the required nodelists including document.getElementById, getElementsByTagName, getElementsByClassName and a method to return elements by CSS selectors.

In fact adding complex CSS-Selector functionality is quite a simple process and for those browsers that support it such as FireFox 3.1, Safari 3.1, Opera 10 and IE 8 in standards mode a function can return nodes matching CSS 3 Selectors with only the following few lines of code which I have placed in a method called selector:
// returns elements by selector e.g DIV P SPAN
// supported in later browsers IE 8 (standards mode), FF 3.1+, Safari 3.1+
selector : function(query){

try {
return  document.querySelectorAll( query )
} catch(e){}

return [];
}


Now we have our main function that instansiates our Getme object and returns various node sets in a multitude of ways we can add our other methods which make use of Javascripts prototype inheritance to add methods to any Getme objects instantiated with the G function call.

All of these functions will return a reference to this (or the Getme object) so that further methods can be appended to the end in a chain. An example of three of these functions are below:

// returns the specified element from the current nodeList
get : function(idx){   

 if(this.nodes && this.nodes.length>0){
  return (typeof(idx)=="number") ? this.nodes[idx] : this.nodes[0];
 }else{
  return null;
 }

},

// sets the innerHTML of one or more nodes.
setHtml : function(content){

 if(content){     
  
  // if a node has been passed in we take its innerHTML
  if(content.nodeType){
   html = content.innerHTML; 
  // otherwise use the string passed to us   
  }else if(typeof(content)==="string"){
   html = content;
  }else{
   html = "";
  }

  // remove all existing html from this node as we are re-setting it
  this.each(function(){
   
   if ( this.hasChildNodes() ){
    while ( this.childNodes.length >= 1 ){
     this.removeChild( this.firstChild );       
    } 
   }
   
   this.innerHTML = html;
  })

 }

 return this;
},

// allows Getme object to reference the static foreach function automatically passing the current node list e.g G('SPAN.myclass').each(function(){})
each : function(callback){

this.foreach(this.nodes,callback);

return this;
},

// allows the setting of style values to the current nodelist
setStyle : function(style, val){

if(style){

var self = this,

atts;

// may have passed att=val pair or a object hash for atts
if(val !== undefined){
// create object hash
atts = eval('({'+style+':"'+val+'"})');
}else{
atts = style;
}

this.each(function(){
self.setProperties.call(this.style,atts);
})
}

return this;
}


As you can see these methods are all chainable in that they can be referenced in the following manner:

G('P > SPAN.info').setHTML('Use the help icon for more info.').setStyle({color:red,fontSize:8px})

I also have added another object called Getme.funcs which holds a number of utility functions that can be called statically without a Getme object reference having to be instantiated. For example in the Getme constructor I make a call to the getElementsByClass method in the following manner:

// get elements by class name
this.nodes = Getme.funcs.getElementsByClassName( match[4],this.context);

You can also call these functions yourself without having to first create a Getme object with the G constructor e.g

// loop through an array of items in arrEls calling a function for each node in the array passed to it
Getme.funcs.foreach(arrEls, function(){ somefunc(arrEls[this] );

I also extend my Getme.prototype object with Getme.funcs once both objects have been defined. This is to allow any Getme objects to have the benefit of having these methods added to their prototype.

The .each function is a good example of a method added to the Getme object as it calls the Getme.funcs.foreach method passing in the current node list stored in this.nodes and a callback function e.g:
// allows Getme object to reference the foreach function automatically passing the current node list e.g G('<pan>').each(function(){})
each : function(callback){

this.foreach(this.nodes,callback);

return this;
}

This allows you to easily call a function for each item in a nodelist and for other methods such as setStyle or setAtts the foreach method is called internally to allow any css style values or attributes to be set for each node element currently stored in the internal this.nodes member.

Although not even nearly complete this object serves as a basic example of how a framework like JQuery operates and you should use this as a guide to creating your own DOM manipulation framework. I have added some more methods to the object which you can see from the full source code which you can download here:


You should also note that to handle CSS 3 Selectors for older browsers such as IE 5-7, FF 1-3, Safari 1-3 etc I have used Sizzle.js which is the same selector engine that JQuery uses.

You can download the version of Sizzle that I have hooked into Getme here:


This brilliant CSS selector engine which was created by John Resig makes CSS 3 Selectors available for all those browsers that don't support the querySelectorAll method available in FireFox 3.2, Safari 3.2, IE 8, Opera 10 and Chrome 3.

You will notice in my Getme constructor that I have the following branch:
// if querySelectorAll is available for modern browsers we can use that e.g
// FF 3.2+, Safari 3.2+, Opera 10, Chrome 3, IE 8 (standards mode)
if(query && this.context === document){
this.nodes = Getme.funcs.selector(sel);
}else{
// otherwise revert to Sizzle which makes a good job of handling older browsers
this.nodes = Getme.find(sel,this.context);
}

Which passes off any selectors for browsers with no querySelectorAll support to Getme.find which is a pointer to the Sizzle function. If you scroll to the bottom of the Sizzle code you will see I have hooked in my Getme object in a similar way as JQuery hooks itself into Sizzle e.g

// EXPOSE Sizzle to Getme.js

window.Sizzle = Sizzle;

Getme.find = Sizzle;
Getme.filter = Sizzle.filter;
Getme.expr = Sizzle.selectors;
Getme.expr[":"] = Getme.expr.filters;
If you wanted to use Sizzle in your own framework or DOM manipulator library then its a simple case of referencing it in your code and either calling the Sizzle functions directly or doing what JQuery and Getme does and point your own functions at Sizzles own functions.

Hopefully this article has been helpful in explaining to new developers how a DOM manipulator library could be build with similar features to other frameworks such as JQuery.

This example Getme.js is not a complete framework and has not been built to become one rather its an example of how easy it is to utilise other useful libraries such as Sizzle and add them to your own code without having to worry about all the bloat that many frameworks come with. You can start off with a basic piece of code to manipulate and search for DOM nodes and then extend it as and when you require new functionality.

Writing your own framework

People always ask me why I don't use one of the big frameworks like jQuery, Prototype, Mootools, YUI etc. My answer is that its not that I don't think their code is good although I have found numerous bugs or issues in all of them over time but rather that I prefer to write my own code because that way I get to understand the language and become a better coder.

I don't claim to be a brilliant Javascript programmer and its only within the last few years that I have developed a big interest in it as opposed to back-end coding and database development. However I know that I won't get to the level of the John Resig's, Dean Edwards and Douglas Crockfords if I always rely on others to write my code for me.

For example on a project I worked on I had to create a fading in lightbox WYSIWYG editor that could float around the screen but never leave the boundaries of the viewport. This is no easy job for a novice and I could have easily loaded up Prototype, Scriptalicious and TinyMCE, found a how to article on Google and got the code working with a few hours. However I would have no idea how the code worked and if I had to make customisations to the code I would then have to spend hours trawling through lots of code I didn't understand hacking about until my tweak was complete. Plus I would have to do it in a way that any updates to those libraries didn't overwrite my changes when future updates were rolled out.

Obviously if you have time constraints or just don't care about how things are done but just want them done in the quickest way possible this maybe the way forward for you and I don't blame you for not wanting to commit the time it would take to build a widget such as the one described from scratch. However this is what I chose to do and it involved spending some considerable time reading up about my intended task before even attempting to write a line of code for the job.

This method of development will take you some time to achieve and you will probably spend a lot of time pulling whatever hair you have left out of your head. However along the way you will learn a lot about your craft including browser differences, the history of the DOM, event models and future developments as well increasing your own skills as a developer.

The pain and time spent will be worth it as when it comes to getting a high paid job in development in the future you will have done yourself a big favour by going down the hard route. Plus the widget or application you have just built will be your own creation and you will know your own code inside out. Any bugs that need fixing will not rely on some 3rd party to release an update and you will have pride in completing a job that many others would not do.

Now I am not totally against frameworks and even use a couple on my own sites. However I do feel that people tend to use them without due consideration to what tasks they actually need to perform. If you are writing heavy DOM manipulated AJAX application that utilise the majority of JQueries features then that is the tool for you. However if you are not intending to use the majority of the features and just like the fact you can access an element by id with the $ sign then you have bloated your codebase unnecessarily.

Whether you write your own code or use frameworks you should at least spend the time looking at the code so that you understand what it is your using and how it works. Once you do this fair play but there is nothing worse in my eyes that someone who uses a framework such as Prototype or Dojo but reverts straight to a forum when they get stuck rather than looking at the source code to see what is happening under the hood. If you at least attempt to do that first and then still don't know what's going on then your well within your rights to ask for help. However at least try to figure out what the code is doing as it may take up some of your time but it may also help you understand JavaScript a little bit more.


If you want to see some ideas on how you could go about writing your own JavaScript framework then check out this article of mine which goes over the basic concepts of using CSS selectors to find your nodes, applying functions to those nodes, chaining functions together, and passing the results of the first function to the second in the chain:

http://blog.strictly-software.com/2009/10/build-your-own-framework.html

Saturday, 3 October 2009

Correctly measuring element dimensions

Obtaining an Elements correct width and height

Should be easy shouldn't it? You could check the elements style.width and style.height property but if they haven't been set by an inline style or with Javascript this won't help. You can use the browser specific style functions to return the current value but there are big differences between IE's currentStyle and the standard getComputedStyle functions.

If you want the height and width in pixels because you need the values for further computations such as positioning an element in the window then you will probably go for offsetWidth and offsetHeight and in fact most of the frameworks will resort to this method within their CSS functions if those dimensions are required. This is because even if you have specified a CSS style for the width and height in pt, em, % or any other unit the offsets will return a value in px. This is very useful as its a very easy method to get an accurate measurement that works cross browser.

Now I was working with some absolutely positioned floating DIV's earlier today and I required the dimensions of an inner DIV for use in a positioning calculation. The issue was that the floating DIV contained numerous inner DIV's only one of which was actually visible at one time. To make one of these sections appear the outer DIV was made visible and all of the inner DIVs apart from the one required to be shown were hidden. When the DIV was closed both the outer and inner DIVs were set to display:none;

I won't go into the reasons for this method apart from saying that the DIV's contained links and the reason I did it this way was for SEO so that all the links were available in the DOM server-side for bots to spider. Too many people nowadays create too much content on the fly with Javascript and forget about the fact that anything created this way is hidden from crawlers.


The problem - Measuring a DIV hidden from the flow

If you want to obtain the dimensions of an element that is currently hidden from the flow using offsetWidth and offsetHeight you must first put that element back into the DOM so that a measurement can be made. The usual method which frameworks like jQuery and Prototype use is to store the existing values for visibility, display and position, set those values to hidden, block and absolute, take a measurement and then revert the styles back to their original values e.g from Prototypes getDimensions function:
getDimensions: function(element) {
element = $(element);
var display = $(element).getStyle('display');
if (display != 'none' && display != null) // Safari bug
return {width: element.offsetWidth, height: element.offsetHeight};

// All *Width and *Height properties give 0 on elements with display none,
// so enable the element temporarily
var els = element.style;
var originalVisibility = els.visibility;
var originalPosition = els.position;
var originalDisplay = els.display;
els.visibility = 'hidden';
els.position = 'absolute';
els.display = 'block';
var originalWidth = element.clientWidth;
var originalHeight = element.clientHeight;
els.display = originalDisplay;
els.position = originalPosition;
els.visibility = originalVisibility;
return {width: originalWidth, height: originalHeight};
},
Now this is fine if the element you are trying to measure is hiding itself from the flow but if a parent is hiding it or even multiple parents then this won't work.

For example if you have a DIV that is set to display:none that contains another DIV also set to display:none; and then this DIV also contains a DIV set to display:none; which is the one you want to measure you actually have to toggle all 3 into the flow and out again to get an accurate offset measurement.

You can see this in action on the following example page I created: http://www.strictly-software.com/testGetdim.htm

View the source and run the test which will take measurements of the DIV on the left which is always in the flow and then the 3 nested DIVs that are hidden from the flow which will only be made visible once the test has run.

As you can see from the debug JQuery has no problem at all getting the measurements of the visible DIV and the outer most one from the 3 nested DIV's. However the two inner DIV's that require parents to be toggled in and out of the flow for their offsets to be measured do not return an accurate value.


Functions to measure offsets correctly

There are 3 main functions used in this test page that return accurate offset values for the nested DIV elements. The others are just helper functions to return a computed/current/style value and output debug or return an element by id.

// returns an array of elements that need to be made visible to carry out a measurement of an element
function getVisibleObj(elem){
arrEls = []; // holds array of elements we need to make visible to measure X

while(elem && elem!==document){
var es = getStyle(elem,"display"); // method returns current/computed/style value

if(es == 'none'){
arrEls.push(elem);
}

elem = elem.parentNode;
}
return arrEls; //null;
}

// swap styles in and out for an accurate measurment. Taken from jQuery and tweaked by myself to
// handle multiple elements.
function Swap(elem, els, styles, callback){

var obj;

for(var x=0,l=els.length;x<l;x++){
// create hash on element to hold old styles so we can revert later
obj = els[x];
obj.old = {};

// Remember the old values, and insert the new ones
for ( var name in styles ) {
obj.old[ name ] = obj.style[ name ];
obj.style[ name ] = styles[ name ];
}
}

// call the function passing in any element that needs scope
callback.call( elem );


for(var x=0,l=els.length;x<l;x++){
obj = els[x];

// Revert the old values
for ( var name in styles ){
obj.style[ name ] = obj.old[ name ];
}
// delete the hash from the element
try{ delete obj.old; }catch(e){ obj.old=null}
}

}

// offsetWidth/Height is element.width/height +border+padding (standard box model)
// clientWidth/Height is element.width/height +padding (if overflow:scroll then -16px)
function getElementDimensions(el){

el = (typeof(el)=="string")?G(el):el; //return a reference to the element

var w=0,h=0,cw=0,ch=0;

// if element is currently hidden we won't be able to get measurements so we need to find out whether this or
// any other parent objects are hiding this element from the flow
var arrEls = getVisibleObj(el); //returns array of objects we need to show to meaure our element

// create function to do the measuring
function getElDim(){

// get style object
var els = el.style;

// get dimensions
w = el.offsetWidth, h = el.offsetHeight, cw = el.clientWidth, ch = el.clientHeight;

}

// do we need to toggle other objects before getting our dimensions
if(arrEls && arrEls.length>0){
// call function to swap over properties so we can accuratley measure this element
var styles = {visibility: "hidden",display:"block"};
Swap(el, arrEls, styles, getElDim);
}else{
getElDim();
}


// create object
var ret = {
"width":w, //total width (element+border+padding)
"height":h,
"clientWidth":cw, //element+padding
"clientHeight":ch
}

return ret;
}

1. getVisibleObj

This function takes an element as its parameter and then loops up any parent elements storing in an array any that have a style.display set to none. Similar function on the web stop at the first element it comes across that is hidden but if you have multiple parents all hiding their children then only a full list will do.

2. Swap

This function was taken from jQuery and tweaked by myself to handle multiple elements instead of just one. The array of elements from getVisibleObj is passed to this function along with the element that requires measuring. This is then passed to the callback function so that correct scope can be utilised not that I require it here. The first loop stores the current style properties of the elements that require toggling in a hash on the object itself. After the callback is run the element styles are toggled back to their original state and this hash is removed from the element.

3. getElementDimensions

This is the function that actually is called by a user and returns an object with the height and width obtained from the elements offset values after any toggling has been carried out.

The reason I have not toggled position:absolute on elements that need toggling is that it seems to work without me doing this plus on the actual code I was working with earlier it was actually causing an incorrect offsetWidth value to be returned in certain situations. The test page seems to work with or without this property so if you have any issues pass this into the Swap function along with display:block and visibility:hidden.

Saturday, 15 August 2009

The large number of duplicate frameworks

Living with multiple frameworks

I have just been investigating some of the source code that some of the new widgets I have added to my site lately contain. Its always good to delve into the source code of any widget or add-on as it helps you as a developer to see other ways of coding including methodology and style. Even with a compressed and packed source code you can easily reformat it to make it readable by using an unpacker tool like www.strictly-software.com/unpack-javascript.

One of the things I noticed was the large amount of duplication in functionality that my site now has due to using these 3rd party add-ons and tools. There is no doubt that these widgets offer some great functionality with little or no cost and development time and they have enhanced the sites usability. However using Googles AJAX API, jQuery, add-this and snapshots and my own core library strictly.js I now have 5 separate JS files being loaded that handle events, load scripts dynamically and other various DOM manipulation functions.

I don't know what the answer is as its too much to ask 3rd party developers to all develop their add-ons with one framework or to ask them to create multiple versions of their widget for each framework. Plus its very likely that any developer who creates a nice add-on will want to write their own code anyway as any coder worth their pay thinks they know best and in most cases they choose to encapsulate any helper functions into the add-ons core files.

Now you could spend a lot of time creating local copies of these add-ons and rewriting all the code to share as much as possible but this would be futile and take a lot of effort. Plus every time a new version came out you would have to go through the same process or otherwise stay with an old version of the software.

I was looking into a really nice looking lightwindow add-on the other day but this involved the installation of another 4 large Javascript files including Prototype and Scriptaculous. Therefore the dilemma was whether to add to the existing frameworks or rewrite the code to use JQuery instead.

This started me thinking around the possibility of some sort of CDN which any developer could access and upload to that would hold files that met the requirements of a standardised API. Developers of new add-ons instead of recreating the wheel could build their widgets using these central objects. It would then be up to the site owner to decide which version of the object he wished to use but as long as any add-ons used on their site implemented this standard API the code would all work.

For example thinking about the common need for event handling by all widgets. Say I am developing a new widget that shows previews of other sites (like the Snapshot add-on). Instead of writing new code to handle event management (add, remove, onload, DOM ready etc) I implement this standard API that has a specific naming convention. There can be multiple objects written to handle events on this CDN and they all differ in their internal workings but they all support the same interface and therefore it would be very easy for a developer to hook into them or change the core framework. An example would be that for any event object written to support this API to add an event they would follow this convention: element.addEvent(type,function,capture).

The main framework developers would all have to standardise their naming conventions if they wanted to use the API but it would mean any site owner using multiple add-ons could choose one core framework for their sites engine and as long as all the add-ons and widgets were built to use the standard API it wouldn't matter if they changed the core framework from JQuery to Prototype the site would continue to work and without the need for code duplication. 3rd party widget developers could specify in their documentation which framework they felt the widget worked best with but it wouldn't depend on that framework and a site owner would make their choice without worrying about breaking the add-ons.

It just an idea that I have just had and it would obviously need lots more thought and the support of the major framework developers as well as any 3rd party widget developers. However until the time comes when some sort of standardised central codebase is implemented and widely used it looks like code duplication and large files are here to stay. Therefore to mitigate this issue site owners will need to make sure all these files are optimised as much as possible through compression, minification, local hosting or CDNs, caching and other tweaks.

This article is a great resource for covering this in detail: http://websitetips.com/optimization/

Sunday, 26 April 2009

Should I use a framework?

When to use a framework and when to write your own code

I am always asked at work why I don't just use a framework such as jQuery, prototype, YUI, MooTools etc rather than spend time writing my own code and its a fair point. I have spent time looking at the major frameworks and its all good code written by clever people and if you haven't got the time to spend then I would definitely recommend using a library. Then again if John Reisig had thought like that then millions of people would be using YUI instead of jQuery and Microsoft would be packaging another library with Visual Studio to handle selectors instead.

Libraries are good for many reasons they hide browser incompatibilities from the developer and its good for a team of developers to stick to a standard code base rather than all adding their own functions and bloating a site up with several versions of the same addEvent or toggleClass function.

The downside is that most libraries will contain lots of code that is never even used by the developer. If you're not even going to be using selectors to return DOM objects in your JavaScript and are just looking for a shorter version of document.getElementById then using $('#blah') is not the way to go. 

The other good thing about writing your own code is that you get to understand the language of your trade a whole lot better than if you just relied on a library. There is no better way in my opinion for learning anything that being thrown in at the deep end and having to sink or swim so to speak. Yes it takes much longer as you will have to read up about the early browser wars and compatibility issues, learn about objects and their properties and understand event models and script syntax but it will make you a much better programmer and when bugs appear due to a new version of Internet Explorer you won't have to wait for an update to your framework to be released.

As with all things its swings and roundabouts and just because I like to write my own code and know why things work the way they do does not mean I won't use a library. However having spent the time researching the language for my own code has given me invaluable knowledge and it helps being able to step through something like jQuery and actually understand what its doing and why rather than just knowing that it works.