Refactoring to Unobtrusive Javascript
Upcoming SlideShare
Loading in...5
×
 

Refactoring to Unobtrusive Javascript

on

  • 9,880 views

Talk i gave at JsCamp09 on September 25th 2009. ...

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

Statistics

Views

Total Views
9,880
Views on SlideShare
9,529
Embed Views
351

Actions

Likes
27
Downloads
162
Comments
3

13 Embeds 351

http://federico.galassi.net 186
http://www.javascriptcamp.com 74
http://paper.li 24
http://www.odino.org 21
http://www.slideshare.net 14
http://www.scoop.it 8
http://a0.twimg.com 7
http://localhost 6
http://twitter.com 4
http://www.linkedin.com 3
https://twitter.com 2
http://us-w1.rockmelt.com 1
http://www.galassi.net 1
More...

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

Refactoring to Unobtrusive Javascript Refactoring to Unobtrusive Javascript Presentation Transcript

  • Refactoring to Unobtrusive Javascript Federico Galassi federico.galassi@gmail.com http://federico.galassi.net/
  • 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 Logic Server side Business Logic
  • Rules of the Game 1 Javascript stays in its own files 2 Files are affected only by changes directly related to presentation logic
  • Game Strategy How do we play? Code refactoring is improving quality of existing code without changing its functional behavior
  • Game Strategy No refactoring without testing • unit testing with jsTestDriver & friends • minimal functional testing with selenium & friends • mock the server by wrapping XMLHttpRequest
  • 1 Round Html You see an <script> doSomething(); // ... more code ... inline script </script>
  • 1 Bad Smell Html <script> doSomething(); Hey, it’s // ... more code ... </script> javascript in html
  • 1 Refactoring: Externalize Inline Script Html Js <script> doSomething(); // ... more code ... </script>
  • 1 Refactoring: Externalize Inline Script Html Js <script src="myjavascript.js"> doSomething(); </script> // ... more code ...
  • 2 Round Html You see an <button onclick="refreshView();"/> event handler registration Refresh </button> by element attribute
  • 2 Bad Smell Html <button onclick="refreshView();"/> Hey, it’s Refresh </button> javascript in html
  • 2 Refactoring: Attribute Event to Dom Event Html Js <button onclick="refreshView();"/> Refresh </button>
  • 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>
  • 3 Round Html You see a javascript link <a href="javascript:showCredits();"> Show Credits</a>
  • 3 Bad Smell Html <a href="javascript:showCredits();"> Hey, it’s Show Credits</a> javascript in html
  • 3 Refactoring: Javascript Link to Click Event Html Js <a href=""> javascript:showCredits(); Show Credits</a>
  • 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>
  • Game Break Now HTML should be Javascript free
  • 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
  • 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
  • 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"; }
  • 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" ); }
  • 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
  • 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
  • 5 Refactoring: Business Logic Simple Test Js Server var account = JSON.parse( response ); if () { account.balance < 0 show("can’t transfer money!"); }
  • 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!"); ?> }
  • 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
  • 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
  • 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);
  • 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);
  • Game Extra Time Make javascript play well with other javascript
  • 7 Round Js var counter = 0; // ... more code ... You see code placed outside a function
  • 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;
  • 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;
  • 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
  • 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
  • 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 // ... }
  • 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 }
  • 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
  • 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!"); }
  • 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"); });
  • 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"); }
  • 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
  • 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
  • 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
  • 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
  • 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
  • Game Over Separation of concerns