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
John Nunemaker
PRO
September 13, 2009
Programming
0
74
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
930
Addicted to Stable
jnunemaker
PRO
32
2.6k
MongoDB for Analytics
jnunemaker
PRO
21
2.3k
MongoDB for Analytics
jnunemaker
PRO
16
30k
Why You Should Never Use an ORM
jnunemaker
PRO
58
9.5k
Why NoSQL?
jnunemaker
PRO
10
940
Don't Repeat Yourself, Repeat Others
jnunemaker
PRO
7
3.5k
I Have No Talent
jnunemaker
PRO
14
970
Other Decks in Programming
See All in Programming
顧客の画像データをテラバイト単位で配信する 画像サーバを WebP にした際に起こった課題と その対応策 ~継続的な取り組みを添えて~
takutakahashi
4
1.3k
Google Agent Development Kit でLINE Botを作ってみた
ymd65536
2
260
レベル1の開発生産性向上に取り組む − 日々の作業の効率化・自動化を通じた改善活動
kesoji
0
300
ニーリーにおけるプロダクトエンジニア
nealle
0
950
テスターからテストエンジニアへ ~新米テストエンジニアが歩んだ9ヶ月振り返り~
non0113
2
220
イベントストーミング図からコードへの変換手順 / Procedure for Converting Event Storming Diagrams to Code
nrslib
2
1.1k
Model Pollution
hschwentner
1
160
#QiitaBash MCPのセキュリティ
ryosukedtomita
1
1.5k
Vibe Codingの幻想を超えて-生成AIを現場で使えるようにするまでの泥臭い話.ai
fumiyakume
9
3.8k
iOS 26にアップデートすると実機でのHot Reloadができない?
umigishiaoi
0
140
NEWT Backend Evolution
xpromx
1
140
20250704_教育事業におけるアジャイルなデータ基盤構築
hanon52_
5
1.1k
Featured
See All Featured
The Invisible Side of Design
smashingmag
301
51k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
35
2.4k
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
30
2.2k
GraphQLとの向き合い方2022年版
quramy
49
14k
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
15
1.6k
KATA
mclloyd
30
14k
Building Better People: How to give real-time feedback that sticks.
wjessup
367
19k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
47
9.6k
Java REST API Framework Comparison - PWX 2021
mraible
31
8.7k
[RailsConf 2023 Opening Keynote] The Magic of Rails
eileencodes
29
9.6k
Balancing Empowerment & Direction
lara
1
450
Music & Morning Musume
bryan
46
6.7k
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