forever-turbolinks 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +537 -0
- data/lib/assets/javascripts/turbolinks.coffee +733 -0
- data/lib/turbolinks.rb +64 -0
- data/lib/turbolinks/cookies.rb +15 -0
- data/lib/turbolinks/redirection.rb +77 -0
- data/lib/turbolinks/version.rb +3 -0
- data/lib/turbolinks/x_domain_blocker.rb +22 -0
- data/lib/turbolinks/xhr_headers.rb +44 -0
- data/lib/turbolinks/xhr_redirect.rb +30 -0
- data/lib/turbolinks/xhr_url_for.rb +23 -0
- data/test/attachment.html +5 -0
- data/test/config.ru +65 -0
- data/test/dummy.gif +0 -0
- data/test/form.html +17 -0
- data/test/index.html +53 -0
- data/test/manifest.appcache +10 -0
- data/test/offline.html +19 -0
- data/test/other.html +26 -0
- data/test/partial1.html +34 -0
- data/test/partial2.html +26 -0
- data/test/partial3.html +28 -0
- data/test/redirect1.html +16 -0
- data/test/redirect2.html +13 -0
- data/test/reload.html +18 -0
- data/test/withoutextension +26 -0
- metadata +113 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a10c1a39d42bed4ede9477083552f772be46aeb1
|
4
|
+
data.tar.gz: 08bee8e1d5f62d1124532912e9b4b1ed39dd3e70
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: d3626b78449132dfa0c383510034a7eac0679872a5fa609fa0b869b02ac60bb3090b425196bc8d10758eefca5a5358fd5feaa6d7773fddb1900295426325bd40
|
7
|
+
data.tar.gz: de17f0d68217e4242fd559154ba8d11e276f626a2b6f7ee085c3e9ca0486a0f7da022f1e5dfcb1e452e70742ba2a238c9fc926af073f9b2ac66a5c2f798ec507
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012-2016 David Heinemeier Hansson
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,537 @@
|
|
1
|
+
Turbolinks Classic is now deprecated
|
2
|
+
====================================
|
3
|
+
|
4
|
+
Turbolinks 5 is a ground-up rewrite with a new flow, new events, but the same core idea. It's available at [turbolinks/turbolinks](https://github.com/turbolinks/turbolinks). This repository remains available for existing applications built on what we now call Turbolinks Classic.
|
5
|
+
|
6
|
+
Turbolinks
|
7
|
+
----------
|
8
|
+
|
9
|
+
Turbolinks makes following links in your web application faster. Instead of letting the browser recompile the JavaScript and CSS between each page change, it keeps the current page instance alive and replaces only the body (or parts of) and the title in the head. Think CGI vs persistent process.
|
10
|
+
|
11
|
+
This is similar to [pjax](https://github.com/defunkt/jquery-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 by default, and let you specify which elements to replace on an opt-in basis. 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.
|
12
|
+
|
13
|
+
Do note that this of course means that you'll have a long-running, persistent session with maintained state. That's what's making it so fast. But it also means that you may have to pay additional care not to leak memory or otherwise bloat that long-running state. That should rarely be a problem unless you're doing something really funky, but you do have to be aware of it. Your memory leaking sins will not be swept away automatically by the cleansing page change any more.
|
14
|
+
|
15
|
+
|
16
|
+
No jQuery or any other library
|
17
|
+
--------------------------------
|
18
|
+
|
19
|
+
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 library to work. But it works great _with_ the jQuery framework, or whatever else you have.
|
20
|
+
|
21
|
+
|
22
|
+
Events
|
23
|
+
------
|
24
|
+
|
25
|
+
With Turbolinks pages will change without a full reload, so you can't rely on `DOMContentLoaded` or `jQuery.ready()` to trigger your code. Instead Turbolinks fires events on `document` to provide hooks into the lifecycle of the page.
|
26
|
+
|
27
|
+
Event | Argument `originalEvent.data` | Notes
|
28
|
+
-------------------- | ----------------------------- | -----
|
29
|
+
`page:before-change` | `{url}` | The page is about to change. **Cancellable with `event.preventDefault()`.** Does not fire on history back/forward.
|
30
|
+
`page:fetch` | `{url}` | A new page is about to be fetched from the server.
|
31
|
+
`page:receive` | `{url}` | A page has been fetched from the server, but not yet parsed.
|
32
|
+
`page:before-unload` | `[affectedNodes]` | Nodes are about to be changed.
|
33
|
+
`page:change` | `[affectedNodes]` | Nodes have changed. **Also fires on `DOMContentLoaded`.**
|
34
|
+
`page:update` | | Fired alongside both `page:change` and jQuery's `ajaxSuccess` (if available).
|
35
|
+
`page:load` | `[newBody]` | A new body element has been loaded into the DOM. **Does not fire on partial replacement or when a page is restored from cache, so as not to fire twice on the same body.**
|
36
|
+
`page:partial-load` | `[affectedNodes]` | New elements have been loaded into the DOM via partial replacement.
|
37
|
+
`page:restore` | | A cached body element has been loaded into the DOM.
|
38
|
+
`page:after-remove` | `affectedNode` | An element has been removed from the DOM or body evicted from the cache and must be cleaned up. jQuery event listeners are cleaned up automatically.
|
39
|
+
|
40
|
+
**Example: load a fresh version of a page from the server**
|
41
|
+
- `page:before-change` link clicked or `Turbolinks.visit()` called (cancellable)
|
42
|
+
- `page:fetch` about to send XHR
|
43
|
+
- `page:receive` received response from server
|
44
|
+
- `page:before-unload` (`[currentBody]`) page has been parsed and is about to be changed
|
45
|
+
- `page:change` (`[newBody]`) new body is in place
|
46
|
+
- `page:update`
|
47
|
+
- `page:load` (`[newBody]`) page has been loaded (progress bar hidden, scroll position updated)
|
48
|
+
- `page:after-remove` (`oldBody`) an old body has been evicted from the cache
|
49
|
+
|
50
|
+
**Example: partial replacement with `Turbolinks.replace()`**
|
51
|
+
- `page:before-unload` (`[currentNodes...]`) nodes are about to be changed
|
52
|
+
- `page:after-remove` (`currentNode`) a node has been removed from the DOM and must be cleaned up (fires once per node)
|
53
|
+
- `page:change` (`[newNodes...]`) new nodes are in place
|
54
|
+
- `page:update`
|
55
|
+
- `page:partial-load` (`[newNodes...]`)
|
56
|
+
|
57
|
+
**Example lifecycle setup:**
|
58
|
+
|
59
|
+
```javascript
|
60
|
+
// using jQuery for simplicity
|
61
|
+
|
62
|
+
$(document).on('ready', function(event) {
|
63
|
+
// initialize persistent state
|
64
|
+
});
|
65
|
+
|
66
|
+
$(document).on('ready page:load', function(event) {
|
67
|
+
// apply non-idempotent transformations to the body
|
68
|
+
});
|
69
|
+
|
70
|
+
$(document).on('page:partial-load', function(event) {
|
71
|
+
// apply non-idempotent transformations to the nodes in event.originalEvent.data
|
72
|
+
});
|
73
|
+
|
74
|
+
$(document).on('page:change', function(event) {
|
75
|
+
// idempotent function
|
76
|
+
});
|
77
|
+
|
78
|
+
$(document).on('page:after-remove', function(event) {
|
79
|
+
// delete references to the nodes in event.originalEvent.data to prevent memory leaks
|
80
|
+
});
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
Page Cache
|
85
|
+
----------
|
86
|
+
|
87
|
+
By default, Turbolinks keeps 10 pages in memory (the full body element is kept in memory, so as not to lose state). On [popstate](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_popstate_event), it attempts to restore pages from the cache. When a page exists in the cache, the following events are triggered:
|
88
|
+
|
89
|
+
- `page:before-unload` (`[currentBody]`) page is about to be changed
|
90
|
+
- `page:change` (`[cachedBody]`) body from cached page is in place
|
91
|
+
- `page:restore`
|
92
|
+
|
93
|
+
The number of pages Turbolinks caches can be configured to suit your application's needs:
|
94
|
+
|
95
|
+
```javascript
|
96
|
+
Turbolinks.pagesCached(); // View the current cache size
|
97
|
+
Turbolinks.pagesCached(20); // Set the cache size
|
98
|
+
```
|
99
|
+
|
100
|
+
If you need to make dynamic HTML updates in the current page and want it to be cached properly you can call:
|
101
|
+
|
102
|
+
```javascript
|
103
|
+
Turbolinks.cacheCurrentPage();
|
104
|
+
```
|
105
|
+
|
106
|
+
**Note:** performing a partial replacement with URL change will remove the current page from the cache. This is because the replaced nodes cannot be brought back. If the user clicks the back button following a visit with partial replacement, the previous page will be fetched from the server.
|
107
|
+
|
108
|
+
|
109
|
+
Transition Cache: A Speed Boost
|
110
|
+
-------------------------------
|
111
|
+
|
112
|
+
Transition Cache makes loading cached pages instantaneous. Once a user has visited a page, returning later to the page results in an instant load.
|
113
|
+
|
114
|
+
For example, if Page A is already cached by Turbolinks and you are on Page B, clicking a link to Page A will *immediately* display the cached copy of Page A. Turbolinks will then fetch Page A from the server and replace the cached page once the new copy is returned.
|
115
|
+
|
116
|
+
To enable Transition Cache, include the following in your javascript:
|
117
|
+
|
118
|
+
```javascript
|
119
|
+
Turbolinks.enableTransitionCache();
|
120
|
+
```
|
121
|
+
|
122
|
+
The one drawback is that dramatic differences in appearance between a cached copy and new copy may lead to a jarring affect for the end-user. This will be especially true for pages that have many moving parts (expandable sections, sortable tables, infinite scrolling, etc.).
|
123
|
+
|
124
|
+
If you find that a page is causing problems, you can have Turbolinks skip displaying the cached copy by adding `data-no-transition-cache` to any DOM element on the offending page.
|
125
|
+
|
126
|
+
|
127
|
+
Progress Bar
|
128
|
+
------------
|
129
|
+
|
130
|
+
Because Turbolinks skips the traditional full page reload, browsers won't display their native progress bar when changing pages. To fill this void, Turbolinks offers a JavaScript-and-CSS-based progress bar to display page loading progress **(as of v3.0, the progress bar is turned on by default)**.
|
131
|
+
|
132
|
+
To disable (or re-enable) the progress bar, include one of the following in your JavaScript:
|
133
|
+
|
134
|
+
```javascript
|
135
|
+
Turbolinks.ProgressBar.disable();
|
136
|
+
Turbolinks.ProgressBar.enable();
|
137
|
+
```
|
138
|
+
|
139
|
+
The progress bar is implemented on the `<html>` element's pseudo `:before` element and can be customized by including CSS with higher specificity than the included styles. For example:
|
140
|
+
|
141
|
+
```css
|
142
|
+
html.turbolinks-progress-bar::before {
|
143
|
+
background-color: red !important;
|
144
|
+
height: 5px !important;
|
145
|
+
}
|
146
|
+
```
|
147
|
+
|
148
|
+
Control the progress bar manually using these methods:
|
149
|
+
|
150
|
+
```javascript
|
151
|
+
Turbolinks.ProgressBar.start();
|
152
|
+
Turbolinks.ProgressBar.advanceTo(value); // where value is between 0-100
|
153
|
+
Turbolinks.ProgressBar.done();
|
154
|
+
```
|
155
|
+
|
156
|
+
|
157
|
+
data-turbolinks-permanent (3.0+)
|
158
|
+
--------------------------------
|
159
|
+
|
160
|
+
DOM elements with `data-turbolinks-permanent` are transferred from page to page (along with all their state). This can make your application even faster by avoiding the need to re-initialize state on certain fixed elements (e.g. a sidebar) after page transitions.
|
161
|
+
|
162
|
+
`data-turbolinks-permanent` must have a unique `id`. You should also make sure that their initialization code is either idempotent or executed only once per Turbolinks session (e.g. on `DOMContentLoaded`).
|
163
|
+
|
164
|
+
|
165
|
+
Initialization
|
166
|
+
--------------
|
167
|
+
|
168
|
+
Turbolinks will be enabled **only** if the server has rendered a `GET` request.
|
169
|
+
|
170
|
+
Why not all request types? Some browsers track the request method of each page load, but triggering `pushState` methods doesn't change this value. This could lead to the situation where pressing the browser's reload button on a page that was fetched with Turbolinks would attempt a `POST` (or something other than `GET`) because the last full page load used that method.
|
171
|
+
|
172
|
+
|
173
|
+
Opting out of Turbolinks
|
174
|
+
------------------------
|
175
|
+
|
176
|
+
By default, all internal HTML links will be funneled through Turbolinks, but you can opt out by marking links or their parent container with `data-no-turbolink`. For example, if you mark a div with `data-no-turbolink`, then all links inside of that div will be treated as regular links. If you mark the body, every link on that entire page will be treated as regular links.
|
177
|
+
|
178
|
+
```html
|
179
|
+
<a href="/">Home (via Turbolinks)</a>
|
180
|
+
<div id="some-div" data-no-turbolink>
|
181
|
+
<a href="/">Home (without Turbolinks)</a>
|
182
|
+
</div>
|
183
|
+
```
|
184
|
+
|
185
|
+
Note that internal links to files containing a file extension other than **.html** will automatically be opted out of Turbolinks. To whitelist additional file extensions to be processed by Turbolinks, use `Turbolinks.allowLinkExtensions()`.
|
186
|
+
|
187
|
+
```javascript
|
188
|
+
Turbolinks.allowLinkExtensions(); // => ['html']
|
189
|
+
Turbolinks.allowLinkExtensions('md'); // => ['html', 'md']
|
190
|
+
Turbolinks.allowLinkExtensions('coffee', 'scss'); // => ['html', 'md', 'coffee', 'scss']
|
191
|
+
```
|
192
|
+
|
193
|
+
Also, Turbolinks is installed as the last click handler for links. So if you install another handler that calls `event.preventDefault()`, Turbolinks will not run. This ensures that you can safely use Turbolinks with things like `data-method`, `data-remote`, or `data-confirm` from Rails.
|
194
|
+
|
195
|
+
**Note:** in v3.0, the default behavior of `redirect_to` is to redirect via Turbolinks on XHR + non-GET requests. You can opt-out of this behavior by passing `turbolinks: false` to `redirect_to`.
|
196
|
+
|
197
|
+
By default, Turbolinks includes itself in `ActionController::Base`. To opt out of the Turbolinks features in certain controllers (`redirect_to` behavior, `request_method` cookie, `X-XHR-Referer` referrer check, etc.), set `config.turbolinks.auto_include` to `false` in `application.rb` and include `Turbolinks::Controller` in the controllers where you use Turbolinks.
|
198
|
+
|
199
|
+
|
200
|
+
jquery.turbolinks
|
201
|
+
-----------------
|
202
|
+
|
203
|
+
If you have a lot of existing JavaScript that binds elements on `jQuery.ready()`, you can pull the [jquery.turbolinks](https://github.com/kossnocorp/jquery.turbolinks) library into your project that will trigger `ready()` when Turbolinks triggers the `page:load` event. It may restore functionality of some libraries.
|
204
|
+
|
205
|
+
Add the gem to your project, then add the following line to your JavaScript manifest file, after `jquery.js` but before `turbolinks.js`:
|
206
|
+
|
207
|
+
``` js
|
208
|
+
//= require jquery.turbolinks
|
209
|
+
```
|
210
|
+
|
211
|
+
Additional details and configuration options can be found in the [jquery.turbolinks README](https://github.com/kossnocorp/jquery.turbolinks/blob/master/README.md).
|
212
|
+
|
213
|
+
|
214
|
+
Asset change detection
|
215
|
+
----------------------
|
216
|
+
|
217
|
+
You can track certain assets, like `application.js` and `application.css`, that you want to ensure are always of the latest version inside a Turbolinks session. This is done by marking those asset links with `data-turbolinks-track`, like so:
|
218
|
+
|
219
|
+
```html
|
220
|
+
<link href="/assets/application-9bd64a86adb3cd9ab3b16e9dca67a33a.css" rel="stylesheet"
|
221
|
+
type="text/css" data-turbolinks-track>
|
222
|
+
```
|
223
|
+
|
224
|
+
If those assets change URLs (embed an md5 stamp to ensure this), the page will do a full reload instead of going through Turbolinks. This ensures that all Turbolinks sessions will always be running off your latest JavaScript and CSS.
|
225
|
+
|
226
|
+
When this happens, you'll technically be requesting the same page twice. Once through Turbolinks to detect that the assets changed, and then again when we do a full redirect to that page.
|
227
|
+
|
228
|
+
|
229
|
+
Evaluating script tags
|
230
|
+
----------------------
|
231
|
+
|
232
|
+
Turbolinks will evaluate any script tags in pages it visits, if those tags do not have a type or if the type is `text/javascript`. All other script tags will be ignored.
|
233
|
+
|
234
|
+
As a rule of thumb when switching to Turbolinks, move all of your javascript tags inside the `head` and then work backwards, only moving javascript code back to the body if absolutely necessary. If you have any script tags in the body you do not want to be re-evaluated then you can set the `data-turbolinks-eval` attribute to `false`:
|
235
|
+
|
236
|
+
```html
|
237
|
+
<script type="text/javascript" data-turbolinks-eval=false>
|
238
|
+
console.log("I'm only run once on the initial page load");
|
239
|
+
</script>
|
240
|
+
```
|
241
|
+
|
242
|
+
Turbolinks will not re-evaluate script tags on back/forward navigation, unless their `data-turbolinks-eval` attribute is set to `always`:
|
243
|
+
|
244
|
+
```html
|
245
|
+
<script type="text/javascript" data-turbolinks-eval=always>
|
246
|
+
console.log("I'm run on every page load, including history back/forward");
|
247
|
+
</script>
|
248
|
+
```
|
249
|
+
|
250
|
+
|
251
|
+
Triggering a Turbolinks visit manually
|
252
|
+
---------------------------------------
|
253
|
+
|
254
|
+
You can use `Turbolinks.visit(path)` to go to a URL through Turbolinks.
|
255
|
+
|
256
|
+
You can also use `redirect_to path, turbolinks: true` in Rails to perform a redirect via Turbolinks.
|
257
|
+
|
258
|
+
|
259
|
+
Partial Replacement (3.0+)
|
260
|
+
--------------------------
|
261
|
+
|
262
|
+
Turbolinks's partial replacement strategy relies on `id` attributes specified on individual nodes or a combination of `id` and `data-turbolinks-permanent` or `data-turbolinks-temporary` attributes.
|
263
|
+
|
264
|
+
```html
|
265
|
+
<div id="comments"></div>
|
266
|
+
<div id="nav" data-turbolinks-permanent></div>
|
267
|
+
<div id="footer" data-turbolinks-temporary></div>
|
268
|
+
```
|
269
|
+
|
270
|
+
Any node with an `id` attribute can be partially replaced. If the `id` contains a colon, the key before the colon can also be targeted to replace many nodes with a similar prefix.
|
271
|
+
|
272
|
+
```html
|
273
|
+
<div id="comments"></div>
|
274
|
+
<div id="comments:123"></div>
|
275
|
+
```
|
276
|
+
|
277
|
+
**Client-side partial replacement**
|
278
|
+
|
279
|
+
`Turbolinks.visit()` should be used when you want to perform an XHR request to fetch the latest content from the server and replace all or some of the nodes.
|
280
|
+
|
281
|
+
`Turbolinks.replace()` should be used when you already have a response body and want to replace the contents of the current page with it. This is needed for contextual responses like validation errors after a failed `create` attempt, since fetching the page again would lose the validation errors.
|
282
|
+
|
283
|
+
```html+erb
|
284
|
+
<body>
|
285
|
+
<div id="sidebar" data-turbolinks-permanent>
|
286
|
+
Never changes after initial load.
|
287
|
+
</div>
|
288
|
+
|
289
|
+
<div id="flash" data-turbolinks-temporary>
|
290
|
+
You have <%= @comments.count %> comments.
|
291
|
+
</div>
|
292
|
+
|
293
|
+
<section id="comments">
|
294
|
+
<%= @comments.each do |comment| %>
|
295
|
+
<article id="comments:<%= comment.id %>">
|
296
|
+
<h1><%= comment.author %></h1>
|
297
|
+
<p><%= comment.body %></p>
|
298
|
+
</article>
|
299
|
+
<% end %>
|
300
|
+
</section>
|
301
|
+
|
302
|
+
<%= form_for Comment.new, remote: true, id: 'new_comment' do |form| %>
|
303
|
+
<%= form.text_area :content %>
|
304
|
+
<%= form.submit %>
|
305
|
+
<% end %>
|
306
|
+
</body>
|
307
|
+
|
308
|
+
<script>
|
309
|
+
// Will change #flash, #comments, #comments:123
|
310
|
+
Turbolinks.visit(url, { change: ['comments'] });
|
311
|
+
|
312
|
+
// Will change #flash, #comments:123
|
313
|
+
Turbolinks.visit(url, { change: ['comments:123'] });
|
314
|
+
|
315
|
+
// Will only keep #sidebar
|
316
|
+
Turbolinks.visit(url)
|
317
|
+
|
318
|
+
// Will only keep #sidebar, #flash
|
319
|
+
Turbolinks.visit(url, { keep: ['flash'] });
|
320
|
+
|
321
|
+
// Will keep nothing
|
322
|
+
Turbolinks.visit(url, { flush: true });
|
323
|
+
|
324
|
+
// Same as visit() but takes a string or Document, allowing you to
|
325
|
+
// do inline responses instead of issuing a new GET with Turbolinks.visit.
|
326
|
+
// This is useful for things like form validation errors or other
|
327
|
+
// contextualized responses.
|
328
|
+
Turbolinks.replace(html, options);
|
329
|
+
</script>
|
330
|
+
```
|
331
|
+
|
332
|
+
**Server-side partial replacement**
|
333
|
+
|
334
|
+
Partial replacement decisions can also be made server-side by using `redirect_to` or `render` with `change`, `append`, `prepend`, `keep`, or `flush` options.
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
class CommentsController < ActionController::Base
|
338
|
+
def index
|
339
|
+
@comments = Comment.page(params[:page]).per(25)
|
340
|
+
|
341
|
+
# Turbolinks appends the nodes in `comment_list`; useful for infinite scrolling
|
342
|
+
render :index, append: ['comment_list']
|
343
|
+
end
|
344
|
+
|
345
|
+
def create
|
346
|
+
@comment = Comment.new(comment_params)
|
347
|
+
|
348
|
+
if @comment.save
|
349
|
+
# This will change #flash, #comments
|
350
|
+
redirect_to comments_url, change: 'comments'
|
351
|
+
# => Turbolinks.visit('/comments', change: ['comments'])
|
352
|
+
else
|
353
|
+
# Validation failure
|
354
|
+
render :new, change: :new_comment
|
355
|
+
# => Turbolinks.replace('<%=j render :new %>', change: ['new_comment'])
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|
359
|
+
```
|
360
|
+
|
361
|
+
```ruby
|
362
|
+
# Redirect via Turbolinks when the request is XHR and not GET.
|
363
|
+
# Refresh any `data-turbolinks-temporary` nodes.
|
364
|
+
redirect_to path
|
365
|
+
|
366
|
+
# Force a redirect via Turbolinks.
|
367
|
+
redirect_to path, turbolinks: true
|
368
|
+
|
369
|
+
# Force a normal redirection.
|
370
|
+
redirect_to path, turbolinks: false
|
371
|
+
|
372
|
+
# Partially replace any `data-turbolinks-temporary` nodes and nodes with `id`s matching `comments` or `comments:*`.
|
373
|
+
redirect_to path, change: 'comments'
|
374
|
+
|
375
|
+
# Partially replace any `data-turbolinks-temporary` nodes and nodes with `id` not matching `something` and `something:*`.
|
376
|
+
redirect_to path, keep: 'something'
|
377
|
+
|
378
|
+
# Replace the entire `body` of the document, including `data-turbolinks-permanent` nodes.
|
379
|
+
redirect_to path, flush: true
|
380
|
+
```
|
381
|
+
|
382
|
+
```ruby
|
383
|
+
# Render with Turbolinks when the request is XHR.
|
384
|
+
# Refresh any `data-turbolinks-temporary` nodes and nodes with `id` matching `new_comment`.
|
385
|
+
render view, change: 'new_comment'
|
386
|
+
|
387
|
+
# Refresh any `data-turbolinks-temporary` nodes and nodes with `id` not matching `something` and `something:*`.
|
388
|
+
render view, keep: 'something'
|
389
|
+
|
390
|
+
# Replace the entire `body` of the document, including `data-turbolinks-permanent` nodes.
|
391
|
+
render view, flush: true
|
392
|
+
|
393
|
+
# Force a render with Turbolinks.
|
394
|
+
render view, turbolinks: true
|
395
|
+
|
396
|
+
# Force a normal render.
|
397
|
+
render view, turbolinks: false
|
398
|
+
```
|
399
|
+
|
400
|
+
**Note:** a request is considered XHR when the `X-Requested-With` header contains `XMLHttpRequest`. This is added automatically by jQuery and other JavaScript frameworks. However, requests made by Turbolinks are not considered XHR on the server.
|
401
|
+
|
402
|
+
Server-side partial replacement was designed to play well with Rails's [`jquery-ujs`](https://github.com/rails/jquery-ujs).
|
403
|
+
|
404
|
+
|
405
|
+
XHR Request Caching (3.0+)
|
406
|
+
--------------------------
|
407
|
+
|
408
|
+
To prevent browsers from caching Turbolinks requests:
|
409
|
+
|
410
|
+
```javascript
|
411
|
+
Turbolinks.disableRequestCaching(); // globally
|
412
|
+
Turbolinks.visit(url, { cacheRequest: false }); // per request
|
413
|
+
```
|
414
|
+
|
415
|
+
This works just like `jQuery.ajax(url, { cache: false })`, appending `"_#{timestamp}"` to the GET parameters.
|
416
|
+
|
417
|
+
|
418
|
+
Client-side API
|
419
|
+
--------------------
|
420
|
+
|
421
|
+
**`Turbolinks`**
|
422
|
+
|
423
|
+
Function | Arguments | Notes
|
424
|
+
----------- | ----------------------------- | -----
|
425
|
+
`visit()` | `path`, `options` | Load a new page and change the URL.
|
426
|
+
`replace()` | `stringOrDocument`, `options` | Replace the current page without changing the URL.
|
427
|
+
|
428
|
+
Option | Type | Notes
|
429
|
+
----------------- | --------------------- | -----
|
430
|
+
`change` | `Array` | Replace only the nodes with the given ids.
|
431
|
+
`append` | `Array` | Append the children of nodes with the given ids.
|
432
|
+
`prepend` | `Array` | Prepend the children of nodes with the given ids.
|
433
|
+
`keep` | `Array` | Replace the body but keep the nodes with the given ids.
|
434
|
+
`flush` | `Boolean` | Replace the body, including `data-turbolinks-permanent` nodes.
|
435
|
+
`title` | `Boolean` or `String` | If `false`, don't update the `document` title. If a string, set the value as title.
|
436
|
+
`scroll` | `Boolean` | If `false`, don't scroll to top (or `#target`) after the page is loaded.
|
437
|
+
`cacheRequest` | `Boolean` | Enable/disable the request cache.
|
438
|
+
`showProgressBar` | `Boolean` | Show/hide the progress bar during the request.
|
439
|
+
|
440
|
+
Function | Arguments | Notes
|
441
|
+
------------------------- | ---------------- | -----
|
442
|
+
`pagesCached()` | None or `Number` | Get or set the maximum number of pages that should be cached.
|
443
|
+
`cacheCurrentPage()` | |
|
444
|
+
`enableTransitionCache()` | |
|
445
|
+
`disableRequestCaching()` | |
|
446
|
+
`allowLinkExtensions()` | `String`... | Whitelist additional file extensions to be processed by Turbolinks.
|
447
|
+
|
448
|
+
Property | Notes
|
449
|
+
----------- | -----
|
450
|
+
`supported` | `true` if the browser fully supports Turbolinks.
|
451
|
+
`EVENTS` | Map of event names.
|
452
|
+
|
453
|
+
**`Turbolinks.ProgressBar`**
|
454
|
+
|
455
|
+
Function | Arguments | Notes
|
456
|
+
------------- | --------- | -----
|
457
|
+
`enable()` | |
|
458
|
+
`disable()` | |
|
459
|
+
`start()` | |
|
460
|
+
`advanceTo()` | `Number` | Value must be between 0 and 100.
|
461
|
+
`done()` | |
|
462
|
+
|
463
|
+
|
464
|
+
Full speed for pushState browsers, graceful fallback for everything else
|
465
|
+
------------------------------------------------------------------------
|
466
|
+
|
467
|
+
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.
|
468
|
+
|
469
|
+
**Note:** there is currenty no fallback for partial replacement on browsers that don't support `pushState`.
|
470
|
+
|
471
|
+
|
472
|
+
Compatibility
|
473
|
+
-------------
|
474
|
+
|
475
|
+
Turbolinks is designed to work with any browser that fully supports `pushState` and all the related APIs. This includes Safari 6.0+ (but not Safari 5.1.x!), IE10, and latest Chromes and Firefoxes.
|
476
|
+
|
477
|
+
Do note that existing JavaScript libraries may not all be compatible with Turbolinks out of the box due to the change in instantiation cycle. You might very well have to modify them to work with Turbolinks's new set of events. For help with this, check out the [Turbolinks Compatibility](http://reed.github.io/turbolinks-compatibility) project.
|
478
|
+
|
479
|
+
Turbolinks works with Rails 3.2 and newer.
|
480
|
+
|
481
|
+
|
482
|
+
Known issues
|
483
|
+
------------
|
484
|
+
|
485
|
+
- External scripts are not guaranteed to execute in DOM order ([#513](https://github.com/turbolinks/turbolinks-classic/issues/513))
|
486
|
+
- Iframes in `data-turbolinks-permanent` nodes are reloaded on page load ([#511](https://github.com/turbolinks/turbolinks-classic/issues/511))
|
487
|
+
- Audio and video elements in `data-turbolinks-permanent` nodes are paused on page load ([#508](https://github.com/turbolinks/turbolinks-classic/issues/508))
|
488
|
+
- Partial replacement removes pages from the cache ([#551](https://github.com/turbolinks/turbolinks-classic/issues/551))
|
489
|
+
|
490
|
+
|
491
|
+
Installation
|
492
|
+
------------
|
493
|
+
|
494
|
+
1. Add `gem 'turbolinks'` to your Gemfile.
|
495
|
+
2. Run `bundle install`.
|
496
|
+
3. Add `//= require turbolinks` to your Javascript manifest file (usually found at `app/assets/javascripts/application.js`). If your manifest requires both turbolinks and jQuery, make sure turbolinks is listed *after* jQuery.
|
497
|
+
4. Restart your server and you're now using turbolinks!
|
498
|
+
|
499
|
+
|
500
|
+
Running the tests
|
501
|
+
-----------------
|
502
|
+
|
503
|
+
Ruby:
|
504
|
+
|
505
|
+
```
|
506
|
+
rake test:all
|
507
|
+
|
508
|
+
BUNDLE_GEMFILE=Gemfile.rails42 bundle
|
509
|
+
BUNDLE_GEMFILE=Gemfile.rails42 rake test
|
510
|
+
```
|
511
|
+
|
512
|
+
JavaScript:
|
513
|
+
|
514
|
+
```
|
515
|
+
bundle install
|
516
|
+
npm install
|
517
|
+
|
518
|
+
script/test # requires phantomjs >= 2.0
|
519
|
+
script/server # http://localhost:9292/javascript/index.html
|
520
|
+
```
|
521
|
+
|
522
|
+
Language Ports
|
523
|
+
--------------
|
524
|
+
|
525
|
+
*These projects are not affiliated with or endorsed by the Rails Turbolinks team.*
|
526
|
+
|
527
|
+
* [Flask Turbolinks](https://github.com/lepture/flask-turbolinks) (Python Flask)
|
528
|
+
* [Django Turbolinks](https://github.com/dgladkov/django-turbolinks) (Python Django)
|
529
|
+
* [ASP.NET MVC Turbolinks](https://github.com/kazimanzurrashid/aspnetmvcturbolinks)
|
530
|
+
* [PHP Turbolinks Component](https://github.com/helthe/Turbolinks) (Symfony Component)
|
531
|
+
* [PHP Turbolinks Package](https://github.com/frenzyapp/turbolinks) (Laravel Package)
|
532
|
+
* [Grails Turbolinks](http://grails.org/plugin/turbolinks) (Grails Plugin)
|
533
|
+
|
534
|
+
Credits
|
535
|
+
-------
|
536
|
+
|
537
|
+
Thanks to Chris Wanstrath for his original work on Pjax. Thanks to Sam Stephenson and Josh Peek for their additional work on Pjax and Stacker and their help with getting Turbolinks released. Thanks to David Estes and Nick Reed for handling the lion's share of post-release issues and feature requests. And thanks to everyone else who's fixed or reported an issue!
|