473,789 Members | 2,634 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ

organizing Javascripts

Dormilich
8,658 Recognized Expert Moderator Expert
Organize your Scripts

what you should have:
  • basic Javascript understanding
  • a standard compliant browser
additional goodies:
  • Firefox with the Firebug plugin installed
1. Javascript, the old way or the inline model

in the early days of Web Coding dynamic functionality was added to a web site using the onEvent attributes.

Expand|Select|Wrap|Line Numbers
  1. <span onclick="doSomething()">click me</span>
thus, for every event you had a separate function which was defined in a script tag (preferably located in the <head>)
Expand|Select|Wrap|Line Numbers
  1. <script type="text/javascript">
  2. function doSomething()
  3. {
  4.     // sample code
  5.     alert("you have successfully clicked me");
  6. }
  7. </script>

2. Play it again, Sam or the traditional model

As time went by, people started to put their code in an external javascript file to keep markup and behaviour separate, because you can maintain your code a lot better this way.

Still the functionality was maintained via the onEvent attribute.
Expand|Select|Wrap|Line Numbers
  1. <script type="text/javascript">
  2. function doSomething()
  3. {
  4.     // sample code
  5.     alert("page finished loading");
  6. }
  7.  
  8. // attach the function to an event attribute
  9. window.onload = doSomething;
  10. </script>
Although the script was better organized this way, you still have the functions hanging around and additionally you have to cope with a new problem. While the inline method directly applied the functionality, an external script has to find the elements that need changing first.

This is where the Document Object Model (DOM) came into play. This may seem tedious work at first, but when you advance within Javascript, you'll see the benefits it brings (no Javascript calls in the HTML code required, inclusion of the Javascript is enough).
Expand|Select|Wrap|Line Numbers
  1. // HTML
  2. <span id="submit">click me</span>
Expand|Select|Wrap|Line Numbers
  1. // Javascript
  2. function doSomething()
  3. {
  4.     // sample code
  5.     alert("you have successfully clicked me");
  6. }
  7. // this will only work, if the element is already loaded into the browser
  8. document.getElementById("submit").onclick = doSomething;
  9.  
  10. // to delay any function call after the page load, you have to use the onload event
  11. window.onload = function()
  12. {
  13.     document.getElementById("submit").onclick = doSomething;
  14. }
Note: the Firebug plugin allows you to view the current document tree (DOM tree).

All this code has a real drawback: you can only apply one function call per event (and element), i.e. if you include several scripts, only the last window.onload call will work (reason, you only have on onEvent attribute per element available).

3. Javascript, next generation or the DOM way of life

All this changed, when the W3C discharged the DOM Level 2. Now you can handle events through functions: addEventListene r & removeEventList ener which bind for a selected event a function to an element.
Expand|Select|Wrap|Line Numbers
  1. function doSomething()
  2. {
  3.     // sample code
  4.     alert("you have successfully clicked me");
  5. }
  6.  
  7. function doSomethingElse()
  8. {
  9.     // code comes here...
  10. }
  11.  
  12. function init()
  13. {
  14.     document.getElementById("submit").addEventListener("click", doSomething, false);
  15.     document.getElementById("submit").addEventListener("click", doSomethingElse, false);
  16. }
  17. window.addEventListener("load", init, false);
Now you can add as many events to an element (from whichever script) as you like.

Note: I only scratch the Event topic here, because it is hardly explained in such a short article, though event handling is at the heart of Javascript (and a good example too).

Further note: I can't cover IE bugs and workarounds here, because that would need an article for itself.

4. Cleaning up the code

there are already good articles out there, howto write good code:
Unobtrusive Javascript
From DHTML to DOM scripting - an example of how to replace outdated JavaScript techniques.
Vitamin Features » The Importance of Maintainable JavaScript

So far we used functions to organize our script, which will be no problem as long as there's not too much code. Imagine you have 3 script files and each of them has its own init() function! this will surely result in problems. So what now? If you have 3rd party scripts, you can't really rewrite their code...

Since Javascript has OOP capabilities (admittedly it's no match to Java) we can use this to stop our functions interfering with each other.

To understand how this works, we have to learn about scope (for instance here: Digital Web Magazine - Scope in JavaScript). Every variable in Javascript has a scope (the context in which it is defined). The important ones are Global Scope (the variable is accessible everywhere in the script) and Local Scope (the variable is accessible only inside the function it was defined in). So any function we define (as we did it) will have Global Scope. In every script file.

So if you have two functions defined, the last defined will win because it will overwrite the former (no matter if they are defined in different files or not).
Expand|Select|Wrap|Line Numbers
  1. function init() 
  2.     alert(1); 
  3. }
  4.  
  5. // somewhere else but later
  6. function init() 
  7.     alert(2); 
  8. }
  9.  
  10. // alerts '2'
  11. init();

Note: While talking about scope, there is also a very useful technique called Closures, which you may find handy (JavaScript Closures for Dummies | Developing thoughts — Morris Johns)

Now that we know about scope, how will that benefit us? The two functions from above had the same name ('init') and the same scope (global). To make them different we need to change at least one of them. You can of course invent countless names including 'init', but we chose this name in the first place, because it described (and still does) the function best.

That's leaving us with the option of changing the scope, but how do we do that? That's where Object Oriented Programming (OOP) comes into play. OOP means dealing with Objects.

In short words, an object is a construct, that has (can have) values (called properties) and functions (called methods) that belong exclusively to that object.

Objects are used as follows:
Expand|Select|Wrap|Line Numbers
  1. // define a new value for the property
  2. object.property = x;
  3.  
  4. // assign the property value to a variable
  5. var y = object.property;
  6.  
  7. // call a method
  8. object.method();
  9.  
  10. // call a method with arguments
  11. object.method(arg1, arg2);
  12.  
  13. // doesn't work
  14. property = x;
  15. var y = property;
  16. method();
If you have your eyes open and your brain on, you'll probably see how this can solve our scope problem. For those left I'll tell you:

the object defines the scope for its methods (functions) and properties (values).

Thus: obj1.init() and obj2.init() are two different functions. Got it?

To solve our scope problem, we just define our functions as methods of different objects.

Note: this is not the original idea of OOP, but I'm not explaining OOP here (it would take too long and it's out of the scope of this article).

Let's get it on. How do we define those objects? There is a way of writing (called the Object Literal (Wait till I come! » Blog Archive » Show love to the object literal)) we can use here.
Expand|Select|Wrap|Line Numbers
  1. var object_name = {
  2.     property_1 : "value_1",
  3.     property_2 : "value_2",
  4.     method_1 : function()
  5.     {
  6.         alert("method 1 executing");
  7.     },
  8.     method_2 : function(x)
  9.     {
  10.         if (this.x)
  11.         {
  12.             alert(this.x);
  13.         }
  14.     }
  15. }
  16.  
  17. // alerts “method 1 executing”
  18. object_name.method_1();
  19.  
  20. // does nothing (value_1 is not the name of a valid property)
  21. object_name.method_2("value_1");
  22.  
  23. // alerts “value_2”
  24. object_name.method_2('property_2');
After knowing how to define objects, we can rewrite the 'init' example from above.
Expand|Select|Wrap|Line Numbers
  1. // since we define the objects (not its methods) in global scope, 
  2. // we can omit the var keyword
  3. NS1 = {
  4.     init : function()
  5.     {
  6.         alert(1);
  7.     }
  8. }
  9.  
  10. NS2 = {
  11.     init : function()
  12.     {
  13.         alert(2);
  14.     }
  15. }
  16.  
  17. // alerts “1”
  18. NS1.init();
  19.  
  20. // alerts “2”
  21. NS2.init();
We now have successfully applied a different scope for each init() function!

5. Structuring the script

When you write a script, then you have (in most cases) only a few number of tasks to do (e.g. event handling (some click events, maybe a mouseover event), dynamic content loading (AJAX, Submenu) or form validation). To get these started, one init() function per task is enough. All the other functions around only help those starter functions getting the work done. So you can easily group the helper functions around the starter functions. And every group can be assigned to its own scope.

Of course you can structure the scopes too … (but that should only be necessary for large libraries)

example:
Expand|Select|Wrap|Line Numbers
  1. myName.menu      // function group for menu creation
  2. myName.forms     // function group for form validation
  3. myName.commons   // functions used in many groups 
  4.                  // e.g. event handling, loop functions, …
If you have read about OOP, you may have heard that an object may possess private properties and methods (the keyword here is ‘(data) encapsulation’) , that can only be called inside the object and are not accessible from outside (no matter what scope it is). Wouldn't it be useful to make all helper functions of a group private (if the function is used only in the group, why granting global access)?

We can do that too. With a little change in the code, we can make public and private properties and methods!

Expand|Select|Wrap|Line Numbers
  1. // our scope (let's call it namespace …)
  2. bytes = {
  3.     // subclasses
  4.     // technically not necessary, but providing an overview
  5.     // esp. if you have many subclasses
  6.     josah : new Object,
  7.     msquared : new Object
  8.  
  9.     // you can define metadata here (dublincore.org)
  10. }
  11.  
  12. // object with only public members
  13. bytes.msquared = {
  14.  
  15.     favouriteColour : "purple",
  16.  
  17.     hateColour : "gray",
  18.  
  19.     state : function()
  20.     {
  21.         alert("I like " + this.favouriteColour);
  22.     },
  23.  
  24.     stateNot : function()
  25.     {
  26.         alert("I hate " + this.hateColour);
  27.     }
  28. }
  29.  
  30. // alerts "I hate gray"
  31. bytes.msquared.stateNot();
  32.  
  33. // alerts "I like blue"
  34. bytes.msquared.favouriteColour = "blue";
  35. bytes.msquared.state();
  36.  
  37. // an object with private and public members
  38. // I didn't go for real functions here……… ;-)
  39. bytes.josah = function() {
  40.  
  41.     // this is a private property
  42.     var hateColour = "purple";
  43.  
  44.     // this is a private function
  45.     // the 'this' keyword does not necessarily
  46.     // point to bytes.josah!
  47.     function throw()
  48.     {
  49.         this.lift();
  50.         this.move();
  51.         this.drop();
  52.     }
  53.  
  54.     return {
  55.  
  56.         // this is a public function
  57.         defenestrate : function(num)
  58.         {
  59.             var OP = thread[num].firstChild;
  60.             OP.addEventListener("unlike", throw, true);
  61.  
  62.             // you can call members from different scopes
  63.             bytes.msquared.hateColour = hateColour;
  64.             bytes.msquared.stateNot();
  65.         }
  66.     };
  67. }();
  68.  
  69. // doesn't work
  70. bytes.josah.throw();
  71.  
  72. // works (but I wouldn't try it)
  73. // defenestrates #666 and alerts "I hate purple"
  74. bytes.josah.defenestrate(666);
now you can group all your functions in classes and you're perfectly organized.

And finally a real world example

Description: popup an image (or anything else you can do within window.open()) when you click on an registered element. there's no constraint, what the element can be, as long as its id attribute is registered for popup. you can even open more than one popup windows, though don't make it too many (the user might get annoyed then).
Expand|Select|Wrap|Line Numbers
  1. /*
  2. #########################################################
  3. #         Popup script by Dormilich, bytes.com          #
  4. # http://bytes.com, If you use this script on your site #
  5. # Please keep this comment at the top of the javascript #
  6. #########################################################
  7. */
  8. Dormilich.Popup = function() {
  9.  
  10.     /**
  11.      * [private] array to store the objects holding the necessary information for window.open()
  12.      */
  13.     var IMG = new Array;
  14.  
  15.     /**
  16.      * [private] calling window.open() on event
  17.      * since the image info object is not part of the element, the correct image object
  18.      * is identified by ID comparison
  19.      *
  20.      * @return (void)
  21.      */
  22.     function pop() 
  23.     {
  24.         var l = IMG.length;
  25.         for (var j=0; j<l; j++) 
  26.         {
  27.             // because pop() is used by addEvent() 'this' points to the object
  28.             // having the event attached to
  29.             if (IMG[j].id == this.id) 
  30.             {
  31.                 window.open(IMG[j].src, IMG[j].titel, IMG[j].props);
  32.             }
  33.         }
  34.     }
  35.  
  36.     return {
  37.  
  38.         /**
  39.          * [public] create an image info object that hold the parameters for window.open() and put
  40.          * it in the private storage array. src, titel & props are the parameters for window.open()
  41.          *
  42.          * @param (string) ID   id of the element to apply the onclick event to
  43.          * @param (string) LOC  the location of the image to popup
  44.          * @param (string) TTL  title of the popup window
  45.          * @param (int) HGT     image height
  46.          * @param (int) WDT     image width
  47.          * @return (void)
  48.          */
  49.         create : function(ID, LOC, TTL, HGT, WDT) 
  50.         {
  51.             var obj = {
  52.                 id    : ID,
  53.                 src   : LOC,
  54.                 titel : TTL,
  55.                 props : "height=" + HGT + ",width=" + WDT + ",menubar=no,resizable=yes,scrollbars=yes"
  56.             }
  57.             IMG.push(obj);
  58.         },
  59.  
  60.         /**
  61.          * [public] loop through all element in the private storage array and attach the private pop() method
  62.          * with the onclick event handler. (interestingly, even though the addEvent() method comes from a
  63.          * different class it's executed in the local scope)
  64.          *
  65.          * @return (void)
  66.          */
  67.         register : function()
  68.         {
  69.             var l = IMG.length;
  70.             for (var i=0; i<l; i++) 
  71.             {
  72.                 var z = document.getElementById(IMG[i].id);  
  73.                 if (z) 
  74.                 {
  75.                     z.style.cursor = "pointer";
  76.                     // currently Tino Zijdel's addEvent() function namespaced
  77.                     Events.addEvent(z, "click", pop);
  78.                 }
  79.             }       
  80.         }
  81.  
  82.     };  
  83. }();
  84.  
  85. Events = {
  86.     addEvent : function(obj, evType, fn, useCapture)
  87.     {
  88.         // full code see http://therealcrisp.xs4all.nl/upload/addEvent2
  89.         // of course you can use any other addEvent() function you like
  90.     }
  91. };
  92.  
sample usage:

Expand|Select|Wrap|Line Numbers
  1. /* Javascript */
  2. // add image
  3. // assigning the image "test.png" to the element with the id "test"
  4. // a new 500x700 popup (inner size) will appear onclick
  5. Dormilich.Popup.create('test', 'test.png', 'TST', 500, 700);
  6. // open a PDF in a new window (just used a random size)
  7. Dormilich.Popup.create('oath', 'slayers_try_prophecy.pdf', 'TRY', 387, 632);
  8. // register elements/images
  9. Events.addEvent(window, "load", Dormilich.Popup.register);
  10.  
  11. /* HTML */
  12. // show a full size image when clicking on a thumbnail
  13. <img src="test_thumb.jpg" width="50" height="70" alt="test image for popup" title="click for full image" id="test" class="thumbnails" />
  14. // show the full text of an essay
  15. <div id="oath" class="shortdesc" title="click for full text">There shall come a controller of a dark star who shall call forth the light. (…)</div>
  16.  
終 (the end)

Dormilich
Dec 21 '08 #1
0 4286

Sign in to post your reply or Sign up for a free account.

Similar topics

3
2427
by: Kerberos | last post by:
When I deliver a page as text/html, the javascripts work, but if delivered as application/xhtml+xml, the javascripts don't work: function OpenBrWindow(theURL,winName,features, myWidth, myHeight, isCenter) { //v3.0 if(window.screen)if(isCenter)if(isCenter=="true"){ var myLeft = (screen.width-myWidth)/2; var myTop = (screen.height-myHeight)/2; features+=(features!='')?',':''; features+=',left='+myLeft+',top='+myTop;
2
2383
by: sdvoranchik | last post by:
We have an application that contains links that run javascripts to create pages in a separate frame. When these links open an external site, it causes the javascripts to no longer function. When we modified the links that invoke the javascripts from: <a href=javascript: top.doSomething> text </a> To: <span onclick=javascript:top.doSomething> text </span> The javascripts worked properly. Any ideas on why this now works or other...
2
1789
by: LC's No-Spam Newsreading account | last post by:
I asked a couple of days ago about the following arrangement (simplified). I made some progress using netscape.security.PrivilegeManager.enablePrivilege but still have to ask some further help. 1) a frameset contains - a number of javascript functions in the head (they do something like top.frames.document.myform.elements.value = v - two frames UP and DOWN
3
4333
by: Jamie | last post by:
Hi, Thanks for the excellent answer to my last question! One more: Does anyone have a method they follow for organizing stylesheets themselves? They seem like they can get bloated and hard to read. Aside from putting all the "h1" rules together, I haven't thought of any way to do it, if it's necessary at all. J.
4
2351
by: David Virgil Hobbs | last post by:
My web host inserts banner ads into my html pages. The javascript in these banner ads interferes with the onload triggered javascript functions in my pages. Whether I trigger my javascript functions through onload=function() in the body tag or through window.onload=function name in a script, the host's advertising javascripts disrupt my javascripts that are supposed to run onload so that they do not run. I am not able to control the...
4
4079
by: swayze | last post by:
Hi, I'm sending some vars to php from javascript. These vars contain special chars (like "&" ,",")and also turkish characters. Therefore I'm using javascripts escape() function to be able to send them properly. My problem is on the php side. I am using rawurldecode to convert them back. However, Turkish characters like "I" (capital "I" with a dot on top) for example still stay encoded as "%u0130". My page is setup to handle turkish...
10
3263
by: Rada Chirkova | last post by:
Hi, at NC State University, my students and I are working on a project called "self-organizing databases," please see description below. I would like to use an open-source database system for implementation and would really appreciate your opinion on whether PostgreSQL is suitable for the project. In general, I am very impressed by the quality of PostgreSQL code and documentation, as well as by the support of the developer community. ...
1
2564
by: Ushach | last post by:
hi, I want to know about Self Organizing maps.T ounderstand the concept ,I need a small example which explains how clustering is done through self organizing maps?If any body implemented it plz send it to me. Thanks in advance, regards, ...
4
1822
by: januarynow | last post by:
Generally, my site contains javascripts (a couple of freebie counters plus some CPM (pay-per-impression) and CPC (pay-per-click) ads), from four different firms, but they are all suffering from the same problem: every day, they have a 90%+ failure rate, in detecting traffic. My server log keeps reporting that numerous people visit the site, but those javascript companies keep saying otherwise. I have reason to believe that the server log is...
0
9511
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can effortlessly switch the default language on Windows 10 without reinstalling. I'll walk you through it. First, let's disable language synchronization. With a Microsoft account, language settings sync across devices. To prevent any complications,...
0
10404
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers, it seems that the internal comparison operator "<=>" tries to promote arguments from unsigned to signed. This is as boiled down as I can make it. Here is my compilation command: g++-12 -std=c++20 -Wnarrowing bit_field.cpp Here is the code in...
1
10136
by: Hystou | last post by:
Overview: Windows 11 and 10 have less user interface control over operating system update behaviour than previous versions of Windows. In Windows 11 and 10, there is no way to turn off the Windows Update option using the Control Panel or Settings app; it automatically checks for updates and installs any it finds, whether you like it or not. For most users, this new feature is actually very convenient. If you want to control the update process,...
0
9979
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each protocol has its own unique characteristics and advantages, but as a user who is planning to build a smart home system, I am a bit confused by the choice of these technologies. I'm particularly interested in Zigbee because I've heard it does some...
0
6765
by: conductexam | last post by:
I have .net C# application in which I am extracting data from word file and save it in database particularly. To store word all data as it is I am converting the whole word file firstly in HTML and then checking html paragraph one by one. At the time of converting from word file to html my equations which are in the word document file was convert into image. Globals.ThisAddIn.Application.ActiveDocument.Select();...
0
5415
by: TSSRALBI | last post by:
Hello I'm a network technician in training and I need your help. I am currently learning how to create and manage the different types of VPNs and I have a question about LAN-to-LAN VPNs. The last exercise I practiced was to create a LAN-to-LAN VPN between two Pfsense firewalls, by using IPSEC protocols. I succeeded, with both firewalls in the same network. But I'm wondering if it's possible to do the same thing, with 2 Pfsense firewalls...
1
4090
by: 6302768590 | last post by:
Hai team i want code for transfer the data from one system to another through IP address by using C# our system has to for every 5mins then we have to update the data what the data is updated we have to send another system
2
3695
muto222
by: muto222 | last post by:
How can i add a mobile payment intergratation into php mysql website.
3
2906
bsmnconsultancy
by: bsmnconsultancy | last post by:
In today's digital era, a well-designed website is crucial for businesses looking to succeed. Whether you're a small business owner or a large corporation in Toronto, having a strong online presence can significantly impact your brand's success. BSMN Consultancy, a leader in Website Development in Toronto offers valuable insights into creating effective websites that not only look great but also perform exceptionally well. In this comprehensive...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.