dieulot / instantclick Goto Github PK
View Code? Open in Web Editor NEWInstantClick makes following links in your website instant.
Home Page: instantclick.io
License: MIT License
InstantClick makes following links in your website instant.
Home Page: instantclick.io
License: MIT License
when I execute <script>document.write(somecode);</script>
before <script src="/static/js/instantclick.js" data-no-instant></script> <script data-no-instant>InstantClick.init('mousedown');</script>
if doesn't work, it just show the content of the document.wrote.
Hi,
could you please add a matrix of supported browsers with level of support? For example currently you have to read through How does it work and then the caniuse pushState
matrix.
I think many clients would appreciate a simple matrix to see whether or not enough browsers (and thus market share on their website) are supported by instantclick to decide whether or not to use instantclick.
-- ooxi
The stylesheets, when included in the <head>
are not loaded when the page is displayed. I had to include them in the <body>
for them to be loaded.
When I load a page with the following code on it:
<td style="background-repeat: no-repeat; background-position: 0px 7px; padding-left: 40px;background-image: url(/some/picture.png);">[..]</td>
then the td-background stays empty in the newest Chromium. Firefox seems to do OK.
When I load a page that has a self-closed textarea on it, it seems as if the textarea is not properly closed, with the rest of the page's code is shown as content of the textarea. Example:
<textarea class="someclass" name="somename" id="someid rows="12" cols="500" /><!-- no closing tag, should be just an empty textarea -->
(it's an XHTML-page)
// InstantClick 1.0
// (C) 2014 Alexandre Dieulot
// http://instantclick.io/license.html
var InstantClick = function() {
var currentPathname = location.pathname
var pId = 0 // short for "preloadId"
var pHistory = {} // short for "preloadHistory"
var p = [] // short for "preloads"
var checkLinkFunction = function() { return true }
function removeHash(url) {
if ( typeof(url)=='undefined' ) { return "" }
var index = url.indexOf('#')
if (index == -1) {
return url
}
return url.substr(0, index)
}
function triggerEvent(name) {
var event = document.createEvent('HTMLEvents')
event.initEvent(name, true, true)
dispatchEvent(event)
}
function debug() {
return {
currentPathname: currentPathname,
p0: p[0],
p1: p[1],
pHistory: pHistory,
pId: pId
}
}
function instantanize(initializing) {
var as = document.getElementsByTagName('a'), a, domain = location.protocol + '//' + location.host
for (var i = as.length - 1; i >= 0; i--) {
a = as[i]
if (a.target || // target="_blank" etc.
/* a.href.indexOf(domain + '/') != 0 || // another domain */
a.href.indexOf('#') > -1 && removeHash(a.href) == removeHash(location.href) || // link to an anchor
a.hasAttribute('data-no-instant') ||
!checkLinkFunction(a.href.substr(domain.length), a)) {
continue
}
a.className +=" preload-attached";
a.addEventListener('mouseover', queue)
a.addEventListener('mouseout', cancle_queue)
a.addEventListener('click', click)
}
if (!initializing) {
var scripts = document.getElementsByTagName('script'), script, copy, parentNode, nextSibling
for (i = scripts.length - 1; i >= 0; i--) {
script = scripts[i]
copy = document.createElement('script')
if (script.src) {
copy.src = script.src
}
if (script.innerHTML) {
copy.innerHTML = script.innerHTML
}
parentNode = script.parentNode
nextSibling = script.nextSibling
parentNode.removeChild(script)
parentNode.insertBefore(copy, nextSibling)
}
}
triggerEvent('page:change')
}
function cancle_queue(e) {
if ( typeof (e.target.timeout ) != 'undefined' ) {
window.clearTimeout( e.target.timeout );
e.target.timeout = undefined;
}
}
function queue(e) {
if ( e.target.className.indexOf(' preloading') > -1 ) {
return
}
console.log( "preloading: " + e.target.href );
delay = 1000;
if ( typeof( localStorage['delay']) != "undefined" ) {
delay = localStorage['delay'] ;
}
e.target.timeout = window.setTimeout( function() {
preload(e.target.href, e.target)
}, delay );
}
function preload(url , e_target) {
var id = pId
/* If the user has clicked on a link but the link's content
has not been received yet, we preload another in parallel,
so that if the first link is taking too much time, the user
can exit by clicking another link, like in a normal browser. */
if (p[pId].state == 'waiting') {
id ^= 1
// The ^ character is used to swap between 0 and 1
}
// set loading state
e_target.className +=" preloading";
p[id].state = 'preloading'
p[id].e_target = e_target;
p[id].url = url
p[id].body = false
p[id].timingStart = +new Date
p[id].timing = false
p[id].xhr.open('GET', url, true)
p[id].xhr.send()
}
function readystatechange(e) {
var id = e.target.id
if (p[id].xhr.readyState < 4) {
return
}
var text = p[id].xhr.responseText
p[id].timing = +new Date - p[id].timingStart
// To debug, we know it has been preloaded if `timing` isn't false
var bodyIndex = text.indexOf('<body')
if (bodyIndex > -1) {
p[id].body = text.substr(text.indexOf('>', bodyIndex) + 1)
var closingIndex = p[id].body.indexOf('</body')
if (closingIndex > -1) {
p[id].body = p[id].body.substr(0, closingIndex)
}
/* Removing script[data-no-instant] */
p[id].body = p[id].body.replace(/<script[^>]+data-no-instant[^>]*>[\s\S]*<\/script>/ig, '')
}
/* We're only getting the body element's innerHTML, not the
element's attributes such as class etc.
From a superficial look into it, it seems Turbolinks DOMify
the body element so it's able to also get classes etc., and
doesn't require an explicit body tag in the html. This
should be explored later. */
var titleIndex = text.indexOf('<title')
if (titleIndex > -1) {
p[id].title = text.substr(text.indexOf('>', titleIndex) + 1)
p[id].title = p[id].title.substr(0, p[id].title.indexOf('</title'))
}
// turn to loaded state
p[id].e_target.className +=" preloaded";
pHistory[removeHash(p[id].url)] = {body: p[id].body, title: p[id].title}
if (id == pId && p[pId].state == 'waiting') {
display(p[pId].url)
}
}
function click(e) {
if (e.which > 1 || e.metaKey || e.ctrlKey) { // Opening in new tab
return
}
e.preventDefault()
display(e.target.href)
}
function display(url) {
// different domain, load normal
domain = location.protocol + '//' + location.host
if ( url.indexOf(domain + '/') != 0 ) {
location.href = url;
return;
}
if (p[pId].url != url) {
if (p[pId ^ 1].url != url) {
//throw new Error('Clicked on a link without hovering on it first')
location.href = url;
}
else {
pId ^= 1
}
}
if (!p[pId].body) {
p[pId].state = 'waiting'
return
}
p[pId].state = 'displayed'
document.body.innerHTML = p[pId].body
document.title = p[pId].title
if (p[pId].url != location.href) {
if (p[pId].url.indexOf('#') == -1) {
scrollTo(0, 0)
}
else {
var elem = p[pId].url.substr(p[pId].url.indexOf('#') + 1)
if (document.getElementById(elem) || document.getElementsByName(elem).length > 0) {
elem = document.getElementById(elem) || document.getElementsByTagName(elem)[0]
var offset = 0
for (; elem.offsetParent; elem = elem.offsetParent) {
offset += elem.offsetTop
}
scrollTo(0, offset)
}
else {
scrollTo(0, 0)
}
}
history.pushState(null, null, p[pId].url)
}
currentPathname = location.pathname
instantanize()
}
function init() {
console.log('I init');
if (!('pushState' in history)) {
return
}
if (p.length) { // already initialized
return
}
if (0 in arguments) {
checkLinkFunction = arguments[0]
}
pHistory[removeHash(location.href)] = {body: document.body.innerHTML, title: document.title}
for (var i = 0; i < 2; i++) {
p[i] = {}
p[i].xhr = new XMLHttpRequest()
p[i].xhr.id = i
p[i].xhr.addEventListener('readystatechange', readystatechange)
p[i].url = false
p[i].body = false
p[i].title = false
p[i].state = ''
p[i].timingStart = false
p[i].timing = false
}
instantanize(true)
addEventListener('popstate', function() {
if (currentPathname == location.pathname) {
return
}
var loc = removeHash(location.href)
if (!(loc in pHistory)) {
location.href = location.href // Reloads the page and makes use of cache for assets, unlike location.reload()
return
}
currentPathname = location.pathname
document.body.innerHTML = pHistory[loc].body + '<script src="instantclick.js" data-no-instant></script>';
document.title = pHistory[loc].title
instantanize()
})
}
return {init: init, debug: debug}
}()
console.log('lib loaded');
InstantClick.init();
While exploring instantclick, I was wondering if instantclick should skip preloading for links that point to the same page. Would this work better because chances of clicking on a link that reference to self is less than other links?
When loading site my Facebook like button works, but when navigating to a new page, I just disapears.
Any way to fix that?
When I am on a slow connection, I hover over a link, it starts to preload but when I click the link it is not preloaded yet (slow network or page) so nothing happens and the page look broken.
There should be a condition that when user clicks a link and the content is not yet preloaded, then a classic navigation to the target pagfe occurs.
Hello again,
I am aware of the change-event. However, I was wondering whether there is a way to automatically reload any other resources in the head-tag such as scripts and CSS (the scripts and stylesheets may vary from page to page)?
That way, this could be dropped-in to any website without having to manually call a ton of JavaScript. For example, when dropping this into one of my projects, I have to manually re-attach events to menus, lightboxes, galleries, tooltips, site-analytics and a range of other things.
I am not sure whether there is a clean way to parse and call the JS that is passed-back from the request, but I just thought I'd put it out there.
Cheers!
Hello, is there a way to delay the changing of the page, for example, to animate the body out? thanks
Really cool idea.
Would it be possible to listen to onclick
``addEventListenerand jQuery's
.click(` navigation?
Here is a rough idea:
data-href
attributes and preload them too - that's a rather common scheme..addEventListener
or jQuery's _data
and checking the event listeners for code containing location( ?)=
or window.location
or location.href
or location.reload(
and if that's an address prefetch itWhat do you think?
So when JS is turned off I display a message on my site.
The style script below should only be parsed by the browser when JS is turned off but I guess InstantClick forces the browser to parse it.
<noscript>
<style type="text/css">header{margin-top:18px}body{margin-top:80px}</style>
<div id="noJS">You have JavaScript disabled. JavaScript must be enabled for this site to function properly.</div>
</noscript>
I got around this bug by adjusting my CSS and removing that style tag altogether but I wanted to report this bug because it might annoy future devs.
Ran into this issue today. On page navigation, Bootstrap-based dropdowns (on click) no longer work. They do work on the initial page, but after any kind of navigation takes place, they no longer appear when their navigation item is clicked..
My HTML structure looks like:
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown</a>
<ul class="dropdown-menu">
<li><a href="/foo">Foo Item</a></li>
<li><a href="/bar">Bar Item</a></li>
</ul>
</li>
I'm loading Instantclick via:
InstantClick.init("mousedown")
I believe this is an Instantclick issue, as the dropdowns worked previously and, as far as I can tell, Instantclick is being initialized correctly.
There is one error in the console, "Uncaught ReferenceError: orientation is not defined " from L533.
Can't isolate the issue, sorry. Removed all other scripts. Unpredictable behavior and unable to properly use in Chrome 32 OSX10.9. Wish I could help more.
Edit - got it working on a different server. Closing, as I cannot isolate. Sorry for the spam post.
i'd love to see a wordpress plugin for this js that enables users to adjust their settings as needed through an options panel. i just tried to set it up on one of my sites but it broke the display of my pages. i'm not code savvy enough to correct this issue on my own.
Although you've got a solution for GA:
< script src="instantclick.min.js" data-no-instant></script>
< script data-no-instant>
/* Google Analytics code here, without ga('send', 'pageview') */
InstantClick.on('change', function() {
ga('send', 'pageview', location.pathname + location.search);
});
InstantClick.init();
</ script>
But it still miss the first page view when user browse the page.
If you just absentmindedly move your cursor around a page for a few seconds, you can end up triggering 4 or 5 requests to the same URL. I get that you don't want "fetch once, cache forever" so that this works well with dynamic content. But firing off multiple requests to the same URL within the span of a second or two is just unnecessary.
I propose that there be a cache that holds on to pages for 5 seconds. If you need your site to be more dynamic than that, well, don't use this script.
Would it be possible to use selectors instead of creating blacklist and whitelist?
I am working on a Wordpress site and I would like to add preload functionality just to some link (i.e. menu links, footer links, content).
Currently, my options are: blacklist all others links (admin bar, link to social networks) or whitelist the ones I am interested in preload.
In both cases I have to work on server-side to add the required attributes to the links.
Wouldn't it be easier to have a parameter to tell the script which links should have the preload? Something like
InstantClick.init('.menu a');
InstantClick.init('.hentry a');
InstantClick.init('.footer a');
This would definitely make easier selecting (and knowing) which links would have the preload capability.
I have seen the plugin uses
document.getElementsByTagName('a')
to retrive all links.
Perhaps
document.querySelectorAll(selector)
would be slower, but would allow more flexibility on the target links.
What do you think?
Hello, I really want to use this script, but when I impliment it on my website, the entire body DOM element is removed and nothing is displayed when I click through to any link. All that remains in the DOM is the HTML, HEAD, and SCRIPT tags. Also, I get a JavaScript error in the browser console:
Uncaught TypeError: Cannot read property 'outerHTML' of null
InstantClick.init
Thanks for your help with this!! :)
I deployed the lib to http://www.kulman.sk according to instructions. There is no JavaScript error and the preloading does not work. I do not see any request on hover like on your demo site.
Without instantclick, if you bind a click
handler to an element and in that handler, call e.preventDefault();
, the browser does not follow that link. You can use that to provide non-javascript-fallback links while still having awesome javascript-dependent on-page features.
When using instantclick, hovever, the preventDefault()
is ignored.
The problem is that instantclick's handler is called before the more specific handler (calling preventDefault
), so isDefaultPrevented()
still returns false
for instantclick.
Consider the issue below which returns undefined.
<a href="http://www.wbur.org/npr/260560847/senate-unexpectedly-moves-forward-on-unemployment-benefits">
<img src="http://media.wbur.org/pagebuilder/461196095_wide-147f0f90e194ad3a715b0f7c3c9f44ed59cfdccf_300.jpg">
</a>
Love the idea, tested out instsantclick on a big site I manage. Chrome Version 32.0.1700.107 m.
I had to remove it though, since the "visited-style" was applied to all links instantclick preloaded. Even the one I ended up not clicking on. Is there a way around that?
I figured using the onmouse-trigger could partly solve it, but then I don't get the biggest speedups either :)
Hi,
this is a UX regression you can see on instantclick.io: if you switch between already loaded pages the progress bar takes longer to fill than the page to swap.
I therefore recommend showing the progressbar not when the loading process starts but after 50ms. And never when switching between already loaded pages.
-- ooxi
Hi. I'm just wondering what we know about browser support. I've noticed there's an issue with Safari 7.0.1 and that looks to be the latest release. That's fine, sh- happens, I'm just curious about what can be expected.
It would be great if instantclick could also start caching the images found in the body of the new document during the preload phase.
It could go through the new body find any img's, extract their src and load them hidden into the current dom.
I'm unsure if you could then rely on the browser cache when swapping the bodies, or whether you'd have to swap the img's in the new body with your caching images.
This should be a toggleable option to avoid using a large amount of bandwidth on image heavy pages (perhaps you whitelist the images?)
del
I'd like to use instantclick's fancy auto-pjax for a reload.
If the use of dynamic content, then it does not work.
I put the integration of "instantclick" library the next project in Magento
http://www.cerebrum.com.br/demo_magento/index.php/
See it by clicking the logo is generated javascript error as image
http://www.cerebrum.com.br/diversos/Captura% 20of% 20the% 20tela% 202014-02-12% 2015:24:43. png
Also we see that the use of "instantclick" library is being generated conflict with the library "rstacruz_nprogress" because it is being displayed the charger but not being completed the same
When i hover multiple times over a link, it re-downloads the page. Thats not necessary, so you should cache allready downloaded pages.
My apologies for not leaving a pull request. This line:
while (target.nodeName != 'A') {
should be:
while (target.nodeName != 'A' && target.nodeName != 'a') {
otherwise the script won't work on my (older XHTML-)pages.
You should also integrate the spinner from nprogress.
I'm testing out the master of IC with the progress bar and the progress bar becomes annoying and flashy when quickly navigating a site.
The spinner should be optional and separated from the progress bar.
P.S. you should separate the CSS from JS.
Mobile all is fine but on my laptop neither firefox or chromium can handle back and forth navigation with instantclick included. Is there a workaround?
Hi,
Do you know if it's possible to configure http://ricostacruz.com/nprogress/ with InstantClick ?
The NProgress.start() can be made in the InstantClick.on "change", but how to trigger the end ?
Thanks.
Hi there,
First of all: I am really liking this so far. Neat idea!
During some initial testing I came across this:
If you have a character such as "โ" in the title tag, it is normally displayed as "Website โ Some Title". However, after loading a page with instantclick, it is shown as "Website โ Some Title".
Anyhow, keep up the great work!
When using the script on the above, on page changes the page will go white unless you refresh the page resulting in the following in console.
TypeError: 'undefined' is not an object (evaluating 'script.hasAttribute') line 117
Line 117: if (script.hasAttribute("data-no-instant"))
This is using the minified production and straight-up development version, error above is using dev version for clarity.
.
hi! thanks for your effort, this project is awesome! ๐
how would be possible to blacklist a determined class in all links of a given site?
lets say the class i want to blacklist would be named "ajax" ...
I test it on two of my tumblr blogs and get the same issue. When click on link I go to a page with only the image not the page.
In other hand It works very well on my personal site: http://microbians.com
Thanks a lot!!
Could this be re-formulated as a polyfill for the spec? Perhaps under a parallel github project...
http://davidwalsh.name/html5-prefetch
If clicking on a https link on a http site or vice versa takes two clicks. The first time the user click nothing happens.
Some places like to abuse GET
requests.
Imagine <a href='/logout'>Log Out</a>
, the users hovers over it, it is instaclicked and the user is logged out. I think there should be a warning about this and possibly a way to opt out it of. (as in, a black list)
Maybe also a black list in the plugin itself to forbid things like:
if([ "login","logout","delete","remove","update","save","submit"].some(x=> href.contains(x))){
//early return
}
Or in ES3 (No lambdas, no some,no contans)
var arr = ["login","logout","delete","remove","update","save","submit"];
for(var i = 0; i < arr.length; i ++){
if(href.indexOf(arr[i]) !== -1){
//early return
}
}
This couple does not work together...
Error: Uncaught Error: History.js Adapter has already been loaded...
What to do? Is it possible to combine?
I am using chrome extension that puts its own dom elements. This library removes those elements after clicking anchor tags.
I like the idea of having a progress bar as you stated on your homepage's "What's next section. Just to add my 0.2 cents, it'd be good regardless of what you decide for the progress bar, for it to be highly customizable. Many people are using the spinners from the FontAwesome icon collection, so it'd be good if your script could accomodate this or other options. Also, it would be good to have onstart
and onended
event listeners to add/remove the progress bars/spinners.
I'm sure you're already thinking of this kind of stuff, but just wanted to chime in.
Thanks for making such a great plugin!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.