turbolinks "instantclick" #313
Comments
I really like this idea! InstantClick feels very fast compared with Turbolinks, especially on a slow 3G connection. In some cases, I think it would make sense to load the page into the cache on hover, but still do a normal request on click, in case the content has changed since hovering. Perhaps there should be an option for this? It would also need to be specifically limited to @sstephenson what are your thoughts? |
If anyone wants a tease at the potential of this, the gist from glennfu works as a drop in example: https://gist.github.com/Enalmada/6e11191c1ea81a75a4f266e147569096. If you were going to use in production you would probably want to modify to not blow out the normal turbolinks cache after lots of hover (to avoid blowing out your backspace position). In the production version of this concept, I believe by default only the latest hover should be cached (which could be a quick fix to the cache blowout) and hover cache should be cleared after click but all this could be configurable depending on how static a site is. |
Would it make sense to roll somethin in like @Enalmada's example behind a flag? Then have users either run |
I decided to try this out and put together this gist based on the one by @Enalmada . I added a check for I still need to look into the cache blowout issue. |
+1 on this, would love to push @Ch4s3 fix to production but am concerned about cache blowout. Is it an idea to clear the cache after x amount of clicks, e.g: call |
@panoply I think |
@Ch4s3 I fall into the same boat, coffeescript is yet to become apart of my knowledge stack. In theory one could easily log x click in conjunction with |
I believe that a low risk high reward default could be precaching only the current hover (re-caching that hover if user moves away and hovers over it again). Average users would feel the extreme "instaclick" performance sensation with negligible risk of stale content. Sites that have mostly mutable links could easily disable it, static content (ex: blog) sites could increase cache size or allow cache to span multiple pages.
(I mean...the gists floating around ALMOST work...the final version just needs make sure precache wont ever blow out normal cache so users don't lose back button page position if they go hover crazy. I assume it might be best to implement with a completely separate precache storage instance that is checked first rather than shared.) Bottom line...could someone with turbolinks experience help take this from gist to something more official? I don't know turbolinks or coffee but after experiencing how amazing "instaclick" makes a site feel with just around 100 lines of code, this is too important to let languish. |
That sounds about like the latency of the server round trip, so its working as expected @Enalmada yeah, I'd be in favor of something low risk, and the more I think about it, only precaching 1 link at a time would be good.
I'd probably prefer to have this be a per link opt in, like my gist. that makes it super narrow, which I like, but you could add it programmatically too.
As stated previously, I'd be happy to help, but I need some direction. |
I guess whether you prefer opt-in or opt-out totally depends on what percentage of your site links have side-effects (or server analytics). I have no doubt some flags could be created to flip it one way or the other to make everyone happy. I wonder what is the best for the average user though. If I had to guess I would say the average link doesn't have side effects...so keep code DRY and follow the turbolinks pattern. |
@Enalmada I agree. Let's take a webshop for example, a user browsing the "products" page are going to be hovering over each product image within a grid and without an opt-in / opt-out this would make for a lot of unnecessary requests. In the use-case scenario an Really interested in exploring this further. |
@panoply That totally makes sense and it will probably be common for people to have special sections of the site they want to preload with care or not at all. The important point here is that hopefully choice can be up to them without making implementation/documentation any more complicated for the majority case. I am just thinking aloud here but perhaps the right thing to do is not define the touchstart/mouseover event handlers in the plugin but allow people to define their own.
Perhaps for the special cases, having an example of something like this could be interesting: jquery-hoverIntent. |
@Ch4s3 not currently. I'd like to see this feature integrated but I am not prepared to learn or get familiar with coffeescript, especially with the advances in ES7 and ES8. I can confirm the methods I have tried in conjunction with Turbolinks increased performance, even the click to action time ratio was increased with preloading on hover, but adding an extra layer on top of Turbolinks is always a mission in itself, the maintainers are super picky. |
@panoply Yeah I'm aware of their general selectiveness. I wonder if it makes sense then to release this as a plugin of sorts. Maybe an npm package and or gem that adds this as a layer on top. That way the maintainers here don't have to take it on in the short run, and we could roll it in ES7. |
@Ch4s3 I would be far more inclined to see this under a plugin as sorts. If we take that route we could introduce something to the prototype property of Turbolinks, because in my head it would make more make more sense to do something like: I'm interested nonetheless, would be happy to help. |
I thought we already had that in Turbolinks |
FYI Until instantclick behavior is turbolinks native (or plugin) ready, you could consider doing a prefetch of any link a user stops moving over:
(Inspired by this question) Note that pages must be browser cacheable to have benefit so dynamic user content pages will need to be something like Cache-Control: private, max-age=5. Todo: hoverintent adds some delay so make it optional by only using it if loaded or opt out class (.prefetch-immediate), offer opt in/out by letting people submit selector (ie a:not[.no-prefetch]), ignore invalid link like hash or javascript placeholder, offer rel=prerender opt in for people who understand the consequences. Perhaps just making this more robust and into a plugin could be the best thing to do for now for the turbolinks community. |
Cleanup up version of above:
// See https://github.com/turbolinks/turbolinks/issues/313
// Using https://briancherne.github.io/jquery-hoverIntent/
// and turbolinks prefetch to make clicks instant
// on page-load and after turbolinks transition
$(document).on("turbolinks:load", function(){
var $prefetcher;
$("a[data-turbolinks!=false]").hoverIntent(function(){
var href = $(this).attr("href");
if(!href.match(/^\//)){ return; } // do not prefetch outside urls or mailto:
// add or change the prefetched link, be careful not to preload the same href multiple times
if ($prefetcher) {
if($prefetcher.attr("href") != href) {
$prefetcher.attr("href", href);
}
} else {
// NOTE: pre-creating the link does not work
$prefetcher = $('<link rel="prefetch" href="' + href + '" />').appendTo("body");
}
});
}); Rails: # Gemfile
gem 'rails-assets-jquery-hoverintent', source: 'https://rails-assets.org' took our site from ~140 mouse-down-to-paint to ~40ms |
Eh, jQuery |
There is also https://github.com/tristen/hoverintent which is a port of jQuery-hoverintent. |
I still need to dig into this as a separate project, it's on my TODO list. |
Here's my stab at this. It has no dependencies. I'd love some extra eyeballs to verify that it works properly. https://gist.github.com/hopsoft/ab500a3b584e2878c83137cb539abb32 Some details on the gist.
|
@hopsoft thanks, i thought it was working for me, but then started to find this kind of errors
|
My bad, this method has been renamed to from fromHTMLElement 84303b9 |
@hopsoft thank you for that I got it to work, but changed it to my needs For me this still downloads the page when the user clicks, even if it already downloaded it during hover prefetch. I suppose this just means that turbolinks uses cache for preview and then refresh when it gets the page ? That's not blocking but could be avoided maybe ? Or am I getting this wrong ? Thanks in advance for sharing some of your insight, I love this feature |
Ah interesting. Yeah the preview makes it feel faster to the user but we could likely avoid the second request altogether. Need to think through how to best accomplish that. The prefetch will warm any Rails caching you have though... making the second request faster. |
The version we have is deeply integrated into our application. What I mean by this is certain actions within the application, reset and/or set preload cache, I need to find the time to break it apart is make it more universal for others to use. |
For the curious, here's an example of a Rails app that uses a modified version of instantclick instead of turbolinks. I couldn't believe that it was a server side rendered Rails app when I first used it. The source code is available here. |
Heh, when I first saw dev.to I thought "why did they bother making this a SPA? It's just a static content site, server-side rendering would have been just fine". |
I've updated the code I've been using for this. Note: This solution works with Safari also. https://gist.github.com/hopsoft/ab500a3b584e2878c83137cb539abb32 |
Just throwing GoogleChromeLabs/quicklink into the mix. It uses a different approach in that it prefetches all links in the viewport, but has some nice features which might be worth integrating:
|
Prefetching all links sounds kinda wasteful especially on larger pages and
in my experience preloading on hover already gives instant feedback, so no
advantage in doing more extreme things
…On Wed, Jan 2, 2019, 10:56 Dom Christie ***@***.*** wrote:
Just throwing GoogleChromeLabs/quicklink
<https://github.com/GoogleChromeLabs/quicklink> into the mix. It uses a
different approach in that it prefetches all links in the viewport, but has
some nice features
<https://github.com/GoogleChromeLabs/quicklink#how-it-works> which might
be worth integrating:
- Waits until the browser is idle (using requestIdleCallback)
- Checks if the user isn't on a slow connection (using
navigator.connection.effectiveType) or has data-saver enabled (using
navigator.connection.saveData)
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#313 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AAAsZycXGtQpNHu3hKKPfQ06PkJvyslHks5u_IJfgaJpZM4OyoOk>
.
|
I agree, and the |
@hopsoft does it still do two requests instead of one ? |
The developer of InstantClick also released a newer, lighter plugin called InstantPage: What would be the best way to integrate this with Turbolinks? EDIT: I've just noticed @david-a-wheeler referenced this above. Sorry for duplicating this. |
@jpic I've since updated the prefetch script I'm using. https://gist.github.com/hopsoft/ab500a3b584e2878c83137cb539abb32 The prefetcher only makes 1 request. It ensures that clicking a prefetched url is handled as a Turbolinks restoration visit. A 2nd request is still made to pick up any page changes since the prefetch cached the Turbolinks snapshot... as per normal Turbolinks behavior. Perceived load time is very fast as it is with any Turbolinks restoration visit. I should reiterate that this solution has no dependencies and also works in Safari. |
@hopsoft .. cool! Can you turn that into a pull request to be included in turbolinks? As long as it can be controlled on a per link basis, I think that would be worth doing. |
Any update on this pull request? |
@hopsoft this is clean. Very clean. |
@hopsoft your work (here: #313 (comment)) is amazing. @sstephenson, @packagethief, @domchristie can we PR that code in a useful feature requested by community for years now? Not everything must always be perfect immediately, I believe, but if we start today then fix/nth PRs will come tomorrow. Thanks for your work. |
Author of instant.page here. Wouldn’t using both Turbolinks and instant.page satisfy your need? I don’t see why they wouldn’t be compatible. There’s also an advantage to using them separately: instant.page is loaded after everything else, so it doesn’t slow down the initial page load. |
@jpic are you using this in production? love it! |
I've got it in production on a few apps. So far so good. |
@hopsoft,
|
@dieulot - I've been assuming that Turbolinks and instant.click are not compatible, since Turbolinks fundamentally changes click behavior and maintains its own cache. If they can simply be installed simultaneously that'd be great, but I don't want to assume that. |
FYI there is a thread on instant.page 's issue tracker regarding compatibility with Turbolinks instantpage/instant.page#52 |
@hopsoft Thank you so much for your snippet. But one question: aren't you getting the security warnings on Rails 5.2 ? I'm getting:
I don't understand why it's triggering this tough, since it's same origin. UPDATE: Found out what the problem was. It was prefetching a controller action that had a response block like this:
And the controller was, as expected, serving the To fix this, I just added a |
I wrote a blog post on my experience setting up InstantClick with Rails (and adding a custom cache to avoid repeating requests on multiple hovers of the same link) in case anybody’s interested: https://dev.to/phacks/the-rocky-road-to-implementing-link-prefetching-in-rails-oo0 |
I wrote a Npm for implement prefetch for Turbolinks:
https://github.com/huacnlee/turbolinks-prefetch import Turbolinks from 'turbolinks';
window.Turbolinks = Turbolinks;
import TurbolinksPrefetch from 'turbolinks-prefetch';
TurbolinksPrefetch.start(); |
I want to use turbolinks but there is something it isn't doing that others seem to be doing...fetching content on hover and using that on click.
This is the main idea behind instantclick.io and the concept seems critical to unlocking the full performance potential of turbolinks. Note that there seems to be some past discussion about prefetching in general that went to dark places (#84) talking about plugins and not universally supported hints. I feel like hints and plugins are overboard...I feel like doing the same thing as instaclick deserves to be natively supported...even the default behavior (with an opt-out attribute for server side analytics or mutable links).
If turbolinks did a fetch of link on hover (rather than click), and used that content on click, it would reduce up to several hundred milliseconds of latency for the average user. It is hard to believe at first but it is true...try it for yourself: http://instantclick.io/click-test. For a properly tuned backend just dealing with network latency, that amount of time can be the difference in user perception between a site being fast and being instant. (Note that barbajs also has similar option)
So turbolinks, can you please consider doing this?
The text was updated successfully, but these errors were encountered: