Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

Already on GitHub? Sign in to your account

Document load order requirements (when anchors.add() should be called) #69

Closed
k-funk opened this Issue Aug 16, 2016 · 8 comments

Comments

Projects
None yet
2 participants
Contributor

k-funk commented Aug 16, 2016 edited

Took me a while to figure out the usage, but in my webpack-built application, I included anchor-js like this:

import anchorJS from 'anchor-js';

/* view wrapper */

    var anchors = new anchorJS();
    anchors.options.placement = 'left';
    anchors.add('article h2, article h4');

    // OR

    var anchors = new anchorJS({placement: 'left'}).add('article h2, article h4');

/* end view wrapper */

I think it would be helpful to add this example to the README, or if it's incorrect, an example of how you think it should be used correctly.
Owner

bryanbraun commented Aug 17, 2016

@k-funk, this is a great idea. We forgot to include more usage examples when we added support for modules (webpack, etc). I think the best place for something like this is on the gh-pages branch, under the "Basic Usage" section, if we can keep it concise, or "Advanced Usage" if we want a full example with options, like yours above.

Contributor

k-funk commented Sep 14, 2016

@bryanbraun Turns out that my example doesn't actually implement all of anchorjs functionality. Linking someone to anchors doesn't work since the id isn't on the page at load time. I actually don't understand how AnchorJS does this in the first place. If the fragment isn't part of the initial html, how does AnchorJS navigate to an ID that it created, after the page is done loading? I don't see any of that in your source files.

Owner

bryanbraun commented Sep 14, 2016

@k-funk, the IDs don't need to be in the original HTML payload. Browsers need to complete a number of actions in the loading lifecycle before they trigger the "jump" behavior based on the URL fragment. For example, they can't jump before the DOM is fully loaded, otherwise they might not find the ID. Also, if they jump too early in the rendering process, they might jump to the wrong place on the page.

All of this gives you time to run JS and add the IDs into the markup.

Different browsers jump at different times, but the recommended way to include AnchorJS is before the closing body tag of your HTML document. This means it'll run early in the process, before DOMContentLoaded is complete.

I've done some rough testing to see "how late" I can run AnchorJS in various browsers and have it "still work". In case you are curious, here's what I found (the browser name is listed where they last successfully jumped to the anchor):

on DOMContentLoaded on window.onload on arbitrary setTimeout
Chrome
Firefox
Safari
IE
Opera

In short, you're good if you run AnchorJS before or on DOMContentLoaded.

The last thing I'll say, is that there's a number of people who use AnchorJS without adding IDs to the page. They often have tools that generate IDs on the backend but they use AnchorJS as an easy way to sprinkle the UI links onto the page. That's a valid use-case that ignores the load-time situation altogether.

@k-funk k-funk added a commit to k-funk/simple-webpack-react-starter that referenced this issue Sep 14, 2016

@k-funk k-funk show anchorjs behavior for bryanbraun/anchorjs#69 1dc007a
Contributor

k-funk commented Sep 14, 2016

I've created a sample repo using webpack/jquery/anchorjs for you to see my behavior.

From what I'm gathering, using jquery's

var anchorJS = require('anchor-js');
var $ = require('jquery');

$(function() {
  new anchorJS({placement: 'right'}).add('h3');
});

is going to be too late?

A lot of web applications start after that event.

If it's required to use

window.addEventListener('DOMContentLoaded', function(){
  new anchorJS({placement: 'right'}).add('h3');
});

then it seems like that should be well documented, since it's very uncommon to listen to that particular event, in my experience.

Owner

bryanbraun commented Sep 19, 2016

@k-funk, I haven't any experienced issues testing it in a DOMContentLoaded callback, so that's why it hasn't been documented as a requirement thus far.

Thanks for putting together that sample repo. I'm in the middle of a busy couple of weeks right now (ran a marathon yesterday, going to a wedding in a couple of days), but I'll test it out the next time I get a chance.

Contributor

k-funk commented Oct 1, 2016

Correct me if I'm wrong, but it seems like the sam issue is happening in your jsfiddle: https://fiddle.jshell.net/bryanbraun/nc6rL9hk/show/light/#return-of-the-jedi
If you view the source of that page, you'll see that it's inside a window.onload=function(){ anchors.add('h2'); }, and that upon refreshing the page with the hash in the url, it doesn't jump to the expected header.

@bryanbraun bryanbraun changed the title from Docs for using anchorjs in node. to Document load order requirements (when anchors.add() should be called) Oct 5, 2016

Owner

bryanbraun commented Oct 5, 2016

@k-funk, ok, I checked out your repo and confirmed that it's not working as expected (and yep, I'm seeing it in that JSFiddle as well). Thanks for putting those together. You're right, we're going to need to add some documentation for this. I've updated the issue title to reflect that (since @afeld put in a PR for the node/commonJS documentation).

I'd like to dig into the details a bit more so we can document it right. A few questions I have are:

  • Is there any concern with triggering this inside jQuery's $(function() { ... }? I was under the impression that it was the same as window.addEventListener('DOMContentLoaded under the hood.
  • When in the event cycle is anchors.add being triggered in your webpack/react demo app? If after window.onload, is that a common usecase?

I'll see what I can find.

We should probably get a better demo than that jsfiddle too... 😆 😞 .

Owner

bryanbraun commented Oct 22, 2016 edited

I'm doing a more accurate round of testing (and adding the test files to the repo). Here's what I found:

before closing body tag on DOMContentLoaded on $(document) .ready on window .onload on arbitrary setTimeout
Chrome
Firefox ✓ (unless it runs after window.onload
Safari
IE ✓ (I can force failure with delays on the page, though (large img)
Opera

The results were consistent with the last set of results, but more detailed. Big takeways:

  1. DOMContentLoaded is consistently successful. $(document).ready (which you used in your demo app) was not.
  2. These events were pretty dependable. I can introduce arbitrary delays into the process and still have the ✓'s and ✘'s hold true. For example, in Chrome it will work for DOMContentLoaded at 2000ms (on a page with lots of DOM processing), but not for window.onload at 200ms (on a very simple page).
  3. I found that triggering on setTimeout wasn't consistent, because it depended on which events had passed at the moment setTimeout fired. Also, $(document.ready), wasn't a consistent event; sometimes it occurred before window.onload and sometimes after. I'd chalk that up to it being a third-party event based on multiple factors, and not a pure browser event.

I've pushed these tests up to the anchorjs repo so people can try them out, if desired (see load-order-1.html, load-order-2.html, and load-order-3.html). Maybe some day, we can automate them.


All-in-all, this is enough to convince me to add documentation, instructing users to trigger the add():

  1. Before the closing body tag, or
  2. on DOMContentLoaded

We can add a warning that including it on $(document).ready or later, could result in the links not jumping properly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment