$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Building Evented Single Page Applications
Search
John Nunemaker
PRO
September 13, 2009
Programming
0
81
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
Atom
jnunemaker
PRO
10
4.3k
MongoDB for Analytics
jnunemaker
PRO
11
1k
Addicted to Stable
jnunemaker
PRO
32
2.7k
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.6k
Why NoSQL?
jnunemaker
PRO
10
970
Don't Repeat Yourself, Repeat Others
jnunemaker
PRO
7
3.5k
I Have No Talent
jnunemaker
PRO
14
1k
Other Decks in Programming
See All in Programming
これだけで丸わかり!LangChain v1.0 アップデートまとめ
os1ma
6
1.7k
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
8
2.2k
ローターアクトEクラブ アメリカンナイト:川端 柚菜 氏(Japan O.K. ローターアクトEクラブ 会長):2720 Japan O.K. ロータリーEクラブ2025年12月1日卓話
2720japanoke
0
720
Integrating WordPress and Symfony
alexandresalome
0
140
【CA.ai #3】ワークフローから見直すAIエージェント — 必要な場面と“選ばない”判断
satoaoaka
0
230
tsgolintはいかにしてtypescript-goの非公開APIを呼び出しているのか
syumai
6
2.1k
Why Kotlin? 電子カルテを Kotlin で開発する理由 / Why Kotlin? at Henry
agatan
2
6.9k
TUIライブラリつくってみた / i-just-make-TUI-library
kazto
1
330
WebRTC、 綺麗に見るか滑らかに見るか
sublimer
1
160
手軽に積ん読を増やすには?/読みたい本と付き合うには?
o0h
PRO
1
170
俺流レスポンシブコーディング 2025
tak_dcxi
13
8.4k
ハイパーメディア駆動アプリケーションとIslandアーキテクチャ: htmxによるWebアプリケーション開発と動的UIの局所的適用
nowaki28
0
380
Featured
See All Featured
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
31
9.8k
Building an army of robots
kneath
306
46k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
21
1.3k
A designer walks into a library…
pauljervisheath
210
24k
Dealing with People You Can't Stand - Big Design 2015
cassininazir
367
27k
Site-Speed That Sticks
csswizardry
13
990
Facilitating Awesome Meetings
lara
57
6.7k
What’s in a name? Adding method to the madness
productmarketing
PRO
24
3.8k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
253
22k
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.6k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
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