Refactoring to
 Unobtrusive
  Javascript
         Federico Galassi
   federico.galassi@gmail.com
    http://federico.galas...
Separation of Concerns



 Keeping different
    aspects of an
application separate
Separation of Concerns


So that everyone can
focus on one thing
      at a time
Unobtrusive Javascript

   Techniques to
enforce separation
 of javascript from
    other web
   technologies
Web Technologies


   Html                 Content

   Css                Presentation

Javascript         Presentation Lo...
Rules of the Game


1 Javascript stays in its own files
2 Files are affected only by
changes directly related to
presentati...
Game Strategy


How do we play?
Code refactoring
is improving quality of
existing code without
changing its functional
   ...
Game Strategy



No refactoring
without testing

• unit testing with jsTestDriver & friends
• minimal functional testing w...
1 Round

                        Html
                                   You see an
<script>
        doSomething();
      ...
1 Bad Smell

                        Html




<script>
        doSomething();



                                         ...
1 Refactoring:
Externalize Inline Script
 Html                                       Js




             <script>
        ...
1 Refactoring:
                     Externalize Inline Script
                        Html                                ...
2 Round

                      Html
                                  You see an
<button
 onclick="refreshView();"/>
     ...
2 Bad Smell

                      Html




<button
 onclick="refreshView();"/>



                                       ...
2 Refactoring:
              Attribute Event to Dom Event
                  Html                                     Js


...
2 Refactoring:
                  Attribute Event to Dom Event
                           Html                             ...
3 Round

                         Html

                                         You see a
                               ...
3 Bad Smell

                         Html



<a

href="javascript:showCredits();">




                                  ...
3 Refactoring:
                   Javascript Link to Click Event
                       Html                              ...
3 Refactoring:
                    Javascript Link to Click Event
                           Html                         ...
Game Break



Now HTML should
be Javascript free
4 Round

                               Js
                                          You see
div.onclick = function(e) {

...
4 Bad Smell

                               Js



div.onclick = function(e) {

    // div has been selected



           ...
4 Refactoring:
                  Dynamic Style to Css Class
                               Js                            C...
4 Refactoring:
                  Dynamic Style to Css Class
                               Js                          Css...
5 Round

                              Js         You test a
var account = JSON.parse(
                                   ...
5 Bad Smell

                                 Js



var account = JSON.parse(
    response
);




                        ...
5 Refactoring:
                     Business Logic Simple Test
                              Js                           ...
5 Refactoring:
                     Business Logic Simple Test
                              Js                           ...
6 Round

                                     Js


// add book to the list
var book = doc.createElement("li");
           ...
6 Bad Smell

                                     Js


// add book to the list

var book = doc.createElement("li");
var ti...
6 Refactoring:
                Dom Creation to Html Template
                          Js                                 ...
6 Refactoring:
                 Dom Creation to Html Template
                                  Js                        ...
Game Extra Time



Make javascript
 play well with
other javascript
7 Round

                       Js
var counter = 0;
// ... more code ...




                              You see code
  ...
7 Bad Smell

                         Js
var counter = 0;
// ... more code ...



// ... later ...
// counter is 1




   ...
7 Refactoring:
                         Global Abatement
                         Js
var counter = 0;
// ... more code ......
7 Refactoring:
                            Global Abatement


                                  Wrap code in
(function() {...
8 Round
                        Login Js
btnLogin.onclick = function(e){
    login();
    toolbar.update();
    logger.log...
8 Bad Smell
                        Login Js
btnLogin.onclick = function(e){
    login();
    toolbar.update();
    logger...
8 Impact
                        Login Js
btnLogin.onclick = function(e){
    login();
    toolbar.update();
    logger.lo...
8 Impact
                        Login Js
btnLogin.onclick = function(e){
    login();
    toolbar.update();
    logger.lo...
8 Refactoring:
                                       Custom Events
                            Login Js


               ...
8 Refactoring:
                                      Custom Events
                           Login Js
 btnLogin.onclick =...
9 Round

                               Js


// home getElementById "tabHome"
// news getElementById "tabNews"
// about ge...
9 Bad Smell

                               Js


// home getElementById "tabHome"
// news getElementById "tabNews"
// abou...
9 Impact

                                  Js


 // home getElementById "tabHome"
 // news getElementById "tabNews"
 // a...
9 Impact

                               Js


// home getElementById "tabHome"




                                       ...
9 Refactoring:
                               Events Delegation
                               Js
                        ...
9 Refactoring:
                              Events Delegation


                                    Handle the event
    ...
Game Over

Separation of concerns
Upcoming SlideShare
Loading in...5
×

Refactoring to Unobtrusive Javascript

8,631

Published on

Talk i gave at JsCamp09 on September 25th 2009.
Slides revised, corrected and expanded.

Also
http://www.javascriptcamp.com/

Follow me on Twitter!
https://twitter.com/federicogalassi

Published in: Technology
3 Comments
29 Likes
Statistics
Notes
No Downloads
Views
Total Views
8,631
On Slideshare
0
From Embeds
0
Number of Embeds
5
Actions
Shares
0
Downloads
166
Comments
3
Likes
29
Embeds 0
No embeds

No notes for slide

Refactoring to Unobtrusive Javascript

  1. 1. Refactoring to Unobtrusive Javascript Federico Galassi federico.galassi@gmail.com http://federico.galassi.net/
  2. 2. Separation of Concerns Keeping different aspects of an application separate
  3. 3. Separation of Concerns So that everyone can focus on one thing at a time
  4. 4. Unobtrusive Javascript Techniques to enforce separation of javascript from other web technologies
  5. 5. Web Technologies Html Content Css Presentation Javascript Presentation Logic Server side Business Logic
  6. 6. Rules of the Game 1 Javascript stays in its own files 2 Files are affected only by changes directly related to presentation logic
  7. 7. Game Strategy How do we play? Code refactoring is improving quality of existing code without changing its functional behavior
  8. 8. Game Strategy No refactoring without testing • unit testing with jsTestDriver & friends • minimal functional testing with selenium & friends • mock the server by wrapping XMLHttpRequest
  9. 9. 1 Round Html You see an <script> doSomething(); // ... more code ... inline script </script>
  10. 10. 1 Bad Smell Html <script> doSomething(); Hey, it’s // ... more code ... </script> javascript in html
  11. 11. 1 Refactoring: Externalize Inline Script Html Js <script> doSomething(); // ... more code ... </script>
  12. 12. 1 Refactoring: Externalize Inline Script Html Js <script src="myjavascript.js"> doSomething(); </script> // ... more code ...
  13. 13. 2 Round Html You see an <button onclick="refreshView();"/> event handler registration Refresh </button> by element attribute
  14. 14. 2 Bad Smell Html <button onclick="refreshView();"/> Hey, it’s Refresh </button> javascript in html
  15. 15. 2 Refactoring: Attribute Event to Dom Event Html Js <button onclick="refreshView();"/> Refresh </button>
  16. 16. 2 Refactoring: Attribute Event to Dom Event Html Js <!-- add id to locate it --> var btnrefresh = <button id="btnRefresh"> document.getElementById( Refresh "btnRefresh" </button> ); btnrefresh.addEventListener( "click", refreshView, false ); <!-- loaded after DOM is built --> <script src="myjavascript.js"> </script> </body> </html>
  17. 17. 3 Round Html You see a javascript link <a href="javascript:showCredits();"> Show Credits</a>
  18. 18. 3 Bad Smell Html <a href="javascript:showCredits();"> Hey, it’s Show Credits</a> javascript in html
  19. 19. 3 Refactoring: Javascript Link to Click Event Html Js <a href=""> javascript:showCredits(); Show Credits</a>
  20. 20. 3 Refactoring: Javascript Link to Click Event Html Js var showcredits = <!-- add id to locate it --> document.getElementById( <a id="linkShowCredits" "linkShowCredits" href="#"> ); Show Credits</a> // in refreshView you should // event.preventDefault showcredits.addEventListener( "click", refreshView, false ); <!-- loaded after DOM is built --> <script src="myjavascript.js"> </script> </body> </html>
  21. 21. Game Break Now HTML should be Javascript free
  22. 22. 4 Round Js You see div.onclick = function(e) { // div has been selected presentation set by var clicked = this; clicked.style.border = "1px solid blue"; element.style } properties
  23. 23. 4 Bad Smell Js div.onclick = function(e) { // div has been selected Hey, it’s var clicked = this; clicked.style.border = "1px solid blue"; css in } javascript
  24. 24. 4 Refactoring: Dynamic Style to Css Class Js Css div.onclick = function(e) { // div has been selected var clicked = this; clicked.style.border = "1px solid blue"; }
  25. 25. 4 Refactoring: Dynamic Style to Css Class Js Css div.onclick = function(e) { // div has been selected var clicked = this; .selected: { // should be addClass border: 1px solid blue; clicked.setAttribute( } "class", "selected" ); }
  26. 26. 5 Round Js You test a var account = JSON.parse( complex boolean response ); if (account.balance < 0) { expression show("can’t transfer money!"); } which is not presentation
  27. 27. 5 Bad Smell Js var account = JSON.parse( response ); Hey, it’s if (account.balance < 0) { show("can’t transfer money!"); } business logic in javascript
  28. 28. 5 Refactoring: Business Logic Simple Test Js Server var account = JSON.parse( response ); if () { account.balance < 0 show("can’t transfer money!"); }
  29. 29. 5 Refactoring: Business Logic Simple Test Js Server var account = JSON.parse( response <?php ); // use business logic to decide if (account.canTransfer) { $account["canTransfer"] = false; echo json_encode($account); show("can’t transfer money!"); ?> }
  30. 30. 6 Round Js // add book to the list var book = doc.createElement("li"); You see complex var title = doc.createElement("strong"); titletext = doc.createTextNode(name); title.appendChild(titletext); var cover = doc.createElement("img"); dom code to cover.src = url; book.appendChild(cover); book.appendChild(title); bookList.appendChild(book); generate html
  31. 31. 6 Bad Smell Js // add book to the list var book = doc.createElement("li"); var title = doc.createElement("strong"); titletext = doc.createTextNode(name); Hey, it’s title.appendChild(titletext); var cover = doc.createElement("img"); cover.src = coverurl; book.appendChild(cover); html in book.appendChild(title); bookList.appendChild(book); javascript
  32. 32. 6 Refactoring: Dom Creation to Html Template Js Html // add book to the list var book = doc.createElement("li"); var title = doc.createElement("strong"); titletext = doc.createTextNode(name); title.appendChild(titletext); var cover = doc.createElement("img"); cover.src = coverurl; book.appendChild(cover); book.appendChild(title); bookList.appendChild(book);
  33. 33. 6 Refactoring: Dom Creation to Html Template Js Html // add book to the list var tplBook = loadTemplate( "book_tpl.html" <li> ); <strong>Title</strong> var book = tplBook.substitute({ <img src="Cover" /> Title: name, </li> Cover: coverurl }); bookList.appendChild(book);
  34. 34. Game Extra Time Make javascript play well with other javascript
  35. 35. 7 Round Js var counter = 0; // ... more code ... You see code placed outside a function
  36. 36. 7 Bad Smell Js var counter = 0; // ... more code ... // ... later ... // counter is 1 Hey, it’s a Other Js // redeclares previous // counter !! global variable var counter = 1;
  37. 37. 7 Refactoring: Global Abatement Js var counter = 0; // ... more code ... // ... later ... // counter is 1 Using the Module pattern Other Js // redeclares previous we can make it private // counter !! var counter = 1;
  38. 38. 7 Refactoring: Global Abatement Wrap code in (function() { Js var counter = 0; // ... more code ... // ... later ... // counter is still 0 })(); an anonymous Other Js function which is immediately // don’t see previous // counter var counter = 1; invoked
  39. 39. 8 Round Login Js btnLogin.onclick = function(e){ login(); toolbar.update(); logger.log("login!"); You see an event handler } function update() { // ... Toolbar Js which calls many unrelated } Logger Js function log(msg) { } // ... modules
  40. 40. 8 Bad Smell Login Js btnLogin.onclick = function(e){ login(); toolbar.update(); logger.log("login!"); } Toolbar Js function update() { Hey, it’s // ... } function log(msg) { Logger Js other modules javascript // ... }
  41. 41. 8 Impact Login Js btnLogin.onclick = function(e){ login(); toolbar.update(); logger.log("login!"); } FAIL Toolbar Js function update() { Time coupling // ... } function log(msg) { Logger Js issues t initialized // ... No }
  42. 42. 8 Impact Login Js btnLogin.onclick = function(e){ login(); toolbar.update(); logger.log("login!"); hange friends.notify("login"); eds C } Ne Toolbar Js function update() { // ... Friends Js Divergent } function notify(event) { // ... dded } A Logger Js function log(msg) { } // ... change
  43. 43. 8 Refactoring: Custom Events Login Js Custom events btnLogin.onclick = function(e){ login(); invert } function update() { toolbar.update(); Toolbar Js dependency and make } code readable Logger Js function log(msg) { logger.log("login!"); }
  44. 44. 8 Refactoring: Custom Events Login Js btnLogin.onclick = function(e){ login(); event.fire("login"); Fire an high level custom } Toolbar Js function update() {...} event.listen("login", function(e) { update(); event and make other modules }); Logger Js listen to it function log(msg) {...} event.listen("login", function(e) { log("login attempt"); });
  45. 45. 9 Round Js // home getElementById "tabHome" // news getElementById "tabNews" // about getElementById "tabAbout" You see many home.onclick = function() { } showPage("pageHome"); news.onclick = function() { similar event handlers showPage("pageNews"); } about.onclick = function() { showPage("pageAbout"); }
  46. 46. 9 Bad Smell Js // home getElementById "tabHome" // news getElementById "tabNews" // about getElementById "tabAbout" home.onclick = function() { showPage("pageHome"); } Hey, it’s news.onclick = function() { showPage("pageNews"); } about.onclick = function() { duplicated showPage("pageAbout"); } javascript
  47. 47. 9 Impact Js // home getElementById "tabHome" // news getElementById "tabNews" // about getElementById "tabAbout" home.onclick = function() { } showPage("pageHome"); More tabs more code news.onclick = function() { showPage("pageNews"); } about.onclick = function() { more handlers showPage("pageAbout"); } contact.onclick = function() { showPage("pageContact"); } Needs Change more memory usage
  48. 48. 9 Impact Js // home getElementById "tabHome" Need to track // news getElementById "tabNews" // about getElementById "tabAbout" home.onclick = function() { showPage("pageHome"); if new elements } news.onclick = function() { showPage("pageNews"); } about.onclick = function() { are added to showPage("pageAbout"); } Contact Js tabContainer.addChild( "tabContact" register handlers ); Missing click handler
  49. 49. 9 Refactoring: Events Delegation Js Event delegation makes code // home getElementById "tabHome" // news getElementById "tabNews" // about getElementById "tabAbout" home.onclick = function() { more compact showPage("pageHome"); } news.onclick = function() { showPage("pageNews"); } and about.onclick = function() { showPage("pageAbout"); } maintainable
  50. 50. 9 Refactoring: Events Delegation Handle the event Js in an elements // container getElementById // "tabContainer" container.onclick = function(e) { ancestor. var id = e.target.id var page = id.replace( "tab", "page" ); showPage(page); Bubbling makes } it work
  51. 51. Game Over Separation of concerns
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×