Skip to content
This repository was archived by the owner on Jul 28, 2018. It is now read-only.

Commit 879db14

Browse files
author
David Heinemeier Hansson
committed
First commit
0 parents  commit 879db14

File tree

7 files changed

+191
-0
lines changed

7 files changed

+191
-0
lines changed

README.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
Turbolinks
2+
===========
3+
4+
Turbolinks makes following links in your web application faster. Instead of letting the browser reload the JavaScript and CSS between each page change, and spend extra HTTP requests checking if the assets are up-to-date, we keep the current instance alive and replace only the body and the title in the head.
5+
6+
This is similar to pjax, but instead of worrying about what element on the page to replace, and tailoring the server-side response to fit, we replace the entire body. This means that you get the bulk of the speed benefits from pjax (no recompiling of the JavaScript or CSS) without having to tailor the server-side response. It just works.
7+
8+
By default, all internal links will be funneled through Turbolinks, but you can opt out by marking links with data-no-turbolink.
9+
10+
11+
No jQuery or any other framework
12+
--------------------------------
13+
14+
Turbolinks is designed to be as light-weight as possible (so you won't think twice about using it even for mobile stuff). It does not require jQuery or any other framework to work. But it works great _with_ jQuery or Prototype or whatever else have you.
15+
16+
17+
The page:update event
18+
---------------------
19+
20+
Since pages will change without a full reload with Turbolinks, you can't by default rely on dom:loaded to trigger your JavaScript code. Instead, Turbolinks uses the page:update event. If you have existing JavaScript code that's made for dom:loaded, you can connect the two using:
21+
22+
```
23+
$(document).on 'page:change', -> $(document).trigger 'dom:loaded'
24+
```
25+
26+
27+
Triggering a Turbolinks visit manually
28+
---------------------------------------
29+
30+
You can use `Turbolinks.visit(path)` to go to a URL through Turbolinks.
31+
32+
33+
Available only for pushState browsers
34+
-------------------------------------
35+
36+
Like pjax, this naturally only works with browsers capable of pushState. But of course we fall back gracefully to full page reloads for browsers that do not support it.
37+
38+
39+
Work left to do
40+
---------------
41+
42+
* CSS/JS asset change detection and reload
43+
* Add a DOM cache for faster back button
44+
* Remember scroll position when using back button
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
visit = (url) ->
2+
if browserSupportsPushState
3+
reflectNewUrl url
4+
fetchReplacement url
5+
else
6+
document.location.href = url
7+
8+
9+
fetchReplacement = (url) ->
10+
xhr = new XMLHttpRequest
11+
xhr.open 'GET', url, true
12+
xhr.onload = -> fullReplacement xhr.responseText, url
13+
xhr.send()
14+
15+
fullReplacement = (html, url) ->
16+
replaceHTML html
17+
triggerPageChange()
18+
19+
reflectNewUrl = (url) ->
20+
window.history.pushState { turbolinks: true }, "", url
21+
22+
triggerPageChange = ->
23+
event = document.createEvent 'Events'
24+
event.initEvent 'page:change', true, true
25+
document.dispatchEvent event
26+
27+
replaceHTML = (html) ->
28+
doc = document.implementation.createHTMLDocument ""
29+
doc.open "replace"
30+
doc.write html
31+
doc.close()
32+
33+
originalBody = document.body
34+
document.documentElement.appendChild doc.body, originalBody
35+
document.documentElement.removeChild originalBody
36+
document.title = title.textContent if title = doc.querySelector "title"
37+
38+
39+
extractLink = (event) ->
40+
link = event.target
41+
until link is this or link.nodeName is 'A'
42+
link = link.parentNode
43+
link
44+
45+
crossOriginLink = (link) ->
46+
location.protocol isnt link.protocol || location.host isnt link.host
47+
48+
anchoredLink = (link) ->
49+
((link.hash && link.href.replace(link.hash, '')) is location.href.replace(location.hash, '')) ||
50+
(link.href is location.href + '#')
51+
52+
noTurbolink = (link) ->
53+
link.getAttribute('data-no-turbolink')?
54+
55+
newTabClick = (event) ->
56+
event.which > 1 || event.metaKey || event.ctrlKey
57+
58+
ignoreClick = (event, link) ->
59+
crossOriginLink(link) || anchoredLink(link) || noTurbolink(link) || newTabClick(event)
60+
61+
handleClick = (event) ->
62+
link = extractLink event
63+
64+
if link.nodeName is 'A' and !ignoreClick(event, link)
65+
visit link.href
66+
event.preventDefault()
67+
68+
69+
browserSupportsPushState =
70+
window.history && window.history.pushState && window.history.replaceState
71+
72+
73+
if browserSupportsPushState
74+
window.addEventListener 'popstate', (event) ->
75+
if event.state?.turbolinks
76+
fetchReplacement document.location.href
77+
78+
document.addEventListener 'click', (event) ->
79+
handleClick event
80+
81+
# Call Turbolinks.visit(url) from client code
82+
@Turbolinks = { visit: visit }

lib/turbolinks.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module Turbolinks
2+
class Engine < ::Rails::Engine
3+
end
4+
end

test/config.ru

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
require 'sprockets'
2+
require 'coffee-script'
3+
4+
Root = File.expand_path("../..", __FILE__)
5+
6+
Assets = Sprockets::Environment.new do |env|
7+
env.append_path Root
8+
end
9+
10+
map "/js" do
11+
run Assets
12+
end
13+
14+
run Rack::Directory.new(Root)

test/index.html

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Home</title>
6+
<script type="text/javascript" src="/js/turbolinks.js"></script>
7+
<script type="text/javascript">
8+
document.addEventListener("page:change", function() {
9+
console.log("page changed");
10+
});
11+
</script>
12+
</head>
13+
<body>
14+
<ul>
15+
<li><a href="/test/other.html">Other page</a></li>
16+
<li><a href="/test/other.html"><span>Wrapped link</span></a></li>
17+
<li><a href="http://www.google.com/">Cross origin</a></li>
18+
</ul>
19+
</body>
20+
</html>

test/other.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Home</title>
6+
<script type="text/javascript" src="/js/turbolinks.js"></script>
7+
<script type="text/javascript">
8+
document.addEventListener("page:change", function() {
9+
console.log("page changed");
10+
});
11+
</script>
12+
</head>
13+
<body>
14+
<ul>
15+
<li><a href="/test/index.html">Home</a></li>
16+
</ul>
17+
</body>
18+
</html>

turbolinks.gemspec

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
Gem::Specification.new do |s|
2+
s.name = 'turbolinks'
3+
s.version = '0.1.0'
4+
s.author = 'David Heinemeier Hansson'
5+
s.email = 'david@loudthinking.com'
6+
s.summary = 'Turbolinks makes following links in your web application faster (use with Rails Asset Pipeline)'
7+
8+
s.files = Dir["lib/assets/javascripts/*.js.coffee", "README.md"]
9+
end

0 commit comments

Comments
 (0)