Turbolinks and beyond
@h6165
Abhishek Yadav
ரூபீ ப்ரோக்ராமர்
Co-organizer: Chennai.js and Chennai.rb
The idea
Turbolinks
- Short-circuit all the links on the page
- Load them using Ajax
- Extract <body> from the response and replace in current page
The goal
Turbolinks
- Make link loads feel faster
- Avoid having to use front-end frameworks
Demo
Turbolinks
- Turbolinks demo
- Pjax demo
Demo
Turbolinks Demo
Pjax Demo
Turbolinks
http://pjax.herokuapp.com
The history
Turbolinks
- pjax (https://github.com/defunkt/jquery-pjax)
- Turbolinks-classic
- Turbolinks-5
The idea
Turbolinks
- Short-circuit all the links on the page - only the application links
- Load them using Ajax - XmlHttpRequest
-
Extract <body> from the response and replace in current page
- Change the page Url
- Hack the back button too
-
Cache visited pages
- Use cached pages for back navigation
- Use cache for temp-preview in forward navigation
- Show a CSS based loader
Problems
Turbolinks
-
Loss of events
- No page load -> no DOMContentLoaded -> no window.onload -> $(document).ready won't work
-
Over-caching
- Back button doesn't reload the previous page - can show stale data
-
Over-scripting
- <script> tags in header and body are evaluated on every page load
- If the script makes a non-idempotent change on the page, it will be repeatedly applied.
Problems
Turbolinks
- Application may need to be rewritten (in parts at least)
- Many plugins can stop working
Problems
Turbolinks
Problems - Solutions
Turbolinks
Loss of events
-
No page load -> no DOMContentLoaded -> no window.onload -> $(document).ready won't work
-
Listen on the turbolinks:load event
-
Use event delegation
-
Use MutationObserver
-
Problems - Loss of events
Turbolinks
Use turbolinks:load
After loading a new page, Turbolinks fires the turbolinks:load event. All/most $(document).ready can be replaced with that -
$(document).ready(function(){
// page load stuff
})
// becomes
$(document).on("turbolinks:load", function(){
// page load stuff
})
// or
document.addEventListener("turbolinks:load", function() {
// page load stuff
})
Problems - Loss of events
Turbolinks
Listening on turbolinks:load is not the best approach.
- Makes the app dependant on library specific event
- Plugins can't use this approach
Problems - Loss of events
Turbolinks
Event delegation
- Is a popular technique (not specific to Turbolinks)
- Attach event to future-elements (elements that may get added via Ajax) by binding to a top level element
- eg - React event handling uses delegation only
- Depends on event bubbling - comes with its own challenges - but is in general more reliable
Problems - Loss of events
Turbolinks
Event delegation
// Instead of -
$('.dance-button').on('click', function(){
// start dancing
})
// This -
// Will work if .dance-button is loaded later by an Ajax call
// Will work with Turbolinks without change
$('body').on('click', '.dance-button' function(){
// start dancing
})
Problems - Loss of events
Turbolinks
Mutation Observers
- Is a somewhat novel technique
- We can observe changes to the DOM and respond to them
- The idea is, DOM content can be changed by entities other than Turbolinks. Like Websockets.
- This could be one place where all the content changes are captured and managed
Problems - Loss of events
Turbolinks
var el = document.getElementById('#my-widget');
// create an observer instance
var observer = new MutationObserver(function(mutations) {
console.log("It changed");
});
observer.observe(el, { childList: true })
Mutation Observers
Problems - Over caching
Turbolinks
Opt out
Cache will not be used in restoration (back button)
or for previews (for visited links)
<meta name="turbolinks-cache-control" content="no-cache">
Problems - Over caching
Turbolinks
Persist elements
- Elements marked as permanent will be left out during page loads
- Suitable for things like cart-counter - whose change logic is independent of page loads
<div id="cart-counter" data-turbolinks-permanent>1 item</div>
Problems - Over scripting
Turbolinks
Idempotent scripts
Scripts that can be run one or many times with the same result
// Overscripting example -
// This is an inline script
<script>
function foo(){
$("#welcome").append("<b> Welcome to our site </b>");
}
function bar(){
$("#welcome").html("<b> Welcome to our site </b>");
}
foo();
bar();
</script>
// foo is not-idempotent
// bar is idempotent
Similar projects
Turbolinks
- Turbolinks classic
- Jquery-turbolinks
- Turbograft
Conclusion
Turbolinks
Why resist client-side Js frameworks - cons
- Your Ain't Gonna Need It Yet (YAGNI) - chances are - you are over-engineering.
-
Cost of addition
- Learning curve
- Api
- Poor UX
- The Just use Jquery philosophy - Its still alive
Conclusion
Turbolinks
Why resist client-side Js frameworks - pros
- Are client-side frameworks the new Jquery ?
- Your app will become complex enough eventually ? Plan for UI complexity
- Resume driven development - if we don't know React, someone else gets the job
Open scenario
Turbolinks
- Form with dropdown
- Subsequent elements change on differing value
Turbolinks and beyond
By Abhishek Yadav
Turbolinks and beyond
- 1,346