Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building Evented Single Page Applications
Search
Sponsored
·
Your Podcast. Everywhere. Effortlessly.
Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
→
John Nunemaker
PRO
September 13, 2009
Programming
0
83
Building Evented Single Page Applications
Given at jQuery conf in Boston.
John Nunemaker
PRO
September 13, 2009
Tweet
Share
More Decks by John Nunemaker
See All by John Nunemaker
AI: The stuff that nobody shows you
jnunemaker
PRO
2
260
Atom
jnunemaker
PRO
10
4.5k
MongoDB for Analytics
jnunemaker
PRO
11
1k
Addicted to Stable
jnunemaker
PRO
32
2.8k
MongoDB for Analytics
jnunemaker
PRO
21
2.3k
MongoDB for Analytics
jnunemaker
PRO
16
30k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.7k
Why NoSQL?
jnunemaker
PRO
10
980
Don't Repeat Yourself, Repeat Others
jnunemaker
PRO
7
3.5k
Other Decks in Programming
See All in Programming
CSC307 Lecture 08
javiergs
PRO
0
670
Rust 製のコードエディタ “Zed” を使ってみた
nearme_tech
PRO
0
180
【卒業研究】会話ログ分析によるユーザーごとの関心に応じた話題提案手法
momok47
0
200
AI Schema Enrichment for your Oracle AI Database
thatjeffsmith
0
290
プロダクトオーナーから見たSOC2 _SOC2ゆるミートアップ#2
kekekenta
0
220
カスタマーサクセス業務を変革したヘルススコアの実現と学び
_hummer0724
0
710
CSC307 Lecture 06
javiergs
PRO
0
690
Smart Handoff/Pickup ガイド - Claude Code セッション管理
yukiigarashi
0
140
ぼくの開発環境2026
yuzneri
0
230
SourceGeneratorのススメ
htkym
0
200
AI Agent の開発と運用を支える Durable Execution #AgentsInProd
izumin5210
7
2.3k
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
390
Featured
See All Featured
Chasing Engaging Ingredients in Design
codingconduct
0
110
Information Architects: The Missing Link in Design Systems
soysaucechin
0
780
Principles of Awesome APIs and How to Build Them.
keavy
128
17k
JAMstack: Web Apps at Ludicrous Speed - All Things Open 2022
reverentgeek
1
340
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
2.1k
Docker and Python
trallard
47
3.7k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
GitHub's CSS Performance
jonrohan
1032
470k
技術選定の審美眼(2025年版) / Understanding the Spiral of Technologies 2025 edition
twada
PRO
117
110k
Testing 201, or: Great Expectations
jmmastey
46
8k
Crafting Experiences
bethany
1
49
Transcript
building evented single-page applications Sunday, September 13, 2009
1. who 2. why 3. demo 4. how Sunday, September
13, 2009
1. who Sunday, September 13, 2009
chancellor http://orderedlist.com/ Sunday, September 13, 2009
professor http://teaching.johnnunemaker.com/ Sunday, September 13, 2009
rubyist http://railstips.org/ Sunday, September 13, 2009
i’m not perfect Sunday, September 13, 2009
2. why Sunday, September 13, 2009
because? Sunday, September 13, 2009
because? Sunday, September 13, 2009
speed only send what changes across the wire Sunday, September
13, 2009
perceived speed Sunday, September 13, 2009
interactivity Sunday, September 13, 2009
experience but the greatest of these is... Sunday, September 13,
2009
3. demo Sunday, September 13, 2009
http://peoplebase.com/ http://harmonyapp.com/ Sunday, September 13, 2009
4. how Sunday, September 13, 2009
goals Sunday, September 13, 2009
no reloads Sunday, September 13, 2009
no reloads history/refresh Sunday, September 13, 2009
no reloads history/refresh easy Sunday, September 13, 2009
# Sunday, September 13, 2009
no reloads history/refresh easy Sunday, September 13, 2009
no reloads history/refresh easy Sunday, September 13, 2009
no reloads history/refresh easy Sunday, September 13, 2009
no reloads history/refresh easy Sunday, September 13, 2009
the end Sunday, September 13, 2009
no reloads Sunday, September 13, 2009
<a href="#/items">Items</a> Sunday, September 13, 2009
$("a[href^='#/']").live('click', function (event) { var $link = $(this); window.location.hash =
$link.attr('href'); $(document).trigger('hashchange'); return false; }); Sunday, September 13, 2009
$(document).bind('hashchange', Layout.reload); Sunday, September 13, 2009
var Layout = { reload: function() { Layout.load(window.location.hash); } };
Sunday, September 13, 2009
var Layout = { load: function(path, options) { path =
path.replace(/^#/, ''); // trigger path loading events $.ajax({ url : path, dataType : 'json', success : function(json) { Layout.onSuccess(json); // trigger path success events if (options && options.success) { options.success(); } }, complete : function() { if (options && options.complete) { options.complete(); } } }); } }; Sunday, September 13, 2009
var Layout = { load: function(path, options) { path =
path.replace(/^#/, ''); $(document).trigger('path:loading', [path]); $(document).trigger('path:loading:' + path); $.ajax({ url: path, dataType: 'json', success: function(json) { Layout.onSuccess(json); $(document).trigger('path:success', [path, json]); $(document).trigger('path:success:' + path, [json]); if (options && options.success) { options.success(); } }, complete: function() { if (options && options.complete) { options.complete(); } } }); } }; Sunday, September 13, 2009
var Layout = { onSuccess: function(json) { Layout.applyJSON(json); // trigger
layout success }, }; Sunday, September 13, 2009
no reloads Sunday, September 13, 2009
history/refresh Sunday, September 13, 2009
setInterval(function() { var hash_is_new = window.location.hash && window.currentHash != window.location.hash;
if (hash_is_new) { window.currentHash = window.location.hash; Layout.handlePageLoad(); } }, 300); Sunday, September 13, 2009
#/org/groups/12/45/new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
org groups 12 45 new Sunday, September 13, 2009
var Layout = { handlePageLoad: function() { var segments =
window.location.hash.replace(/^#\//, '').split('/'), total = segments.length, path = ''; function loadSectionsInOrder() { var segment = segments.shift(); path += '/' + segment; var onComplete = function() { var loaded = total - segments.length, finished = loaded == total; if (!finished) { loadSectionsInOrder(); } }; Layout.load(path, {complete: onComplete}); } loadSectionsInOrder(); } }; Sunday, September 13, 2009
var Layout = { handlePageLoad: function() { var segments =
window.location.hash.replace(/^#\//, '').split('/'), total = segments.length, path = ''; $(document).trigger('page:loading'); function loadSectionsInOrder() { var segment = segments.shift(); path += '/' + segment; var onComplete = function() { var loaded = total - segments.length, finished = loaded == total; $(document).trigger('page:progress', [total, loaded]); if (finished) { $(document).trigger('page:loaded'); } else { loadSectionsInOrder(); } }; Layout.load(path, {complete: onComplete}); } loadSectionsInOrder(); } }; Sunday, September 13, 2009
$(document).bind('page:progress', function(e, total, loaded) { if (total != loaded) {
var final_width = 114, new_width = (loaded/total) * final_width; $('#loading_progress').animate({width: new_width+'px'}, 200); } }); $(document).bind('page:loading', function() { $('#harmony_loading').show(); $('#loading_progress').css('width', 0); }); $(document).bind('page:loaded', function() { $('#loading_progress').animate({width:'114px'}, 300, 'linear', function() { $('#harmony_loading').hide(); }); }); Sunday, September 13, 2009
history/refresh Sunday, September 13, 2009
easy Sunday, September 13, 2009
rule #1: abuse events for better code separation and easier
customization http://orderedlist.com/articles/jquery-evented-programming-primer Sunday, September 13, 2009
$('form').live('submit', function(event) { var $form = $(this); $form.ajaxSubmit({ dataType: 'json',
beforeSend: function() { // trigger before send }, success: function(json) { // if errors, show them, else apply json and reset form }, error: function(response, status, error) { // trigger error }, complete: function() { // trigger complete } }); return false; }); Sunday, September 13, 2009
$('form').live('submit', function(event) { var $form = $(this); $form.ajaxSubmit({ dataType: 'json',
beforeSend: function() { $form.trigger('form:beforeSend'); }, success: function(json) { if (json.errors) { $form.showErrors(json.errors); } else { Layout.onSuccess(json); $form.trigger('form:success', [json]); $form.resetForm(); } }, error: function(response, status, error) { $form.trigger('form:error', [response, status, error]); }, complete: function() { $form.trigger('form:complete'); } }); return false; }); Sunday, September 13, 2009
var Site = { onCreateSuccess: function(event, json) { Sidebar.highlight($('#site_' +
json.id)) Layout.updateHashWithoutLoad(window.location.hash.replace(/new$/, json.id)); }, onUpdateSuccess: function(event, json) { Sidebar.highlight($('#site_' + json.id)) } }; $('form.new_site').live('form:success', Site.onCreateSuccess); $('form.edit_site').live('form:success', Site.onUpdateSuccess); Sunday, September 13, 2009
var Field = { onCreateSuccess: function(event, json) { $('#list_section_' +
json.section_id) .addSectionField(json.field_title.toLowerCase()); }, onUpdateSuccess: function(event, json) { $('#list_section_' + json.section_id) .removeSectionField(json.old_title.toLowerCase()) .addSectionField(json.new_title.toLowerCase()); } }; $('form.new_field') .live('form:success', Field.onCreateSuccess); $('form.edit_field').live('form:success', Field.onUpdateSuccess); Sunday, September 13, 2009
Layout.applyJSON accepts json like below and “applies” it to the
layout; gets called with each url load either from click or form Sunday, September 13, 2009
Layout.applyJSON { 'replace': { '#content': '<h1>Heading</h1><p>Yay!</p>' }, 'replaceWith': { '#post_12':
'<div class="post">...</div>' }, 'remove': ['#post_11', '#comment_12'] } Sunday, September 13, 2009
rule #2: the url dictates everything Sunday, September 13, 2009
var Layout = { livePath: function(event, path, callback) { if
(typeof(test) === 'string') { $(document).bind('path:' + event + ':' + path, callback); } else { Layout.live_path_regex[event].push([path, callback]); } } }; Sunday, September 13, 2009
Layout.livePath('loading', /\/org\/([^\/]+)([0-9\/]+).*/, Groups.loading); Layout.livePath('loading', /\/sections\/([0-9]+)$/, Sections.markCurrent); Layout.livePath('success', '/org/groups', Groups.setup); Layout.livePath('success',
'/sections', Sections.makeSortable); Layout.livePath('success', /\/admin\/assets(.*)/, Assets.init); Layout.livePath('success', /\/admin\/items\/(\d+)/, ItemForm.init); Layout.livePath('success', /\/admin\/items\/([0-9\/]+)/, Items.manageSidebar); Sunday, September 13, 2009
rule #3: do it live Sunday, September 13, 2009
$('a.field_form_toggler') .live('click', Fields.toggleAddFieldForm); $('form.new_section') .live('form:success', SectionForm.onCreateSuccess); $('form.edit_section') .live('form:success', SectionForm.onUpdateSuccess); $('form.new_field')
.live('form:success', Field.onCreateSuccess); $('form.edit_field') .live('form:success', Field.onUpdateSuccess); $('a.field_destroy') .live('destroy:success', Field.onDestroySuccess); Sunday, September 13, 2009
easy Sunday, September 13, 2009