Skip to content
New issue

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

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enable controlling precise timing of 3rd-party script loading #68

Closed
jab opened this issue Sep 22, 2015 · 9 comments
Closed

Enable controlling precise timing of 3rd-party script loading #68

jab opened this issue Sep 22, 2015 · 9 comments

Comments

@jab
Copy link
Contributor

jab commented Sep 22, 2015

It can be necessary to include third-party script in a page, and control the timing of when it loads very precisely.

Google Analytics provides one example: It's recommended to include the Google Analytics snippet just before </head>, so that it has a chance of counting the pageview as early as possible, but to load it asynchronously, so that it doesn't block rendering of the rest of the page. The DOM element for this script should be included in the initial static html of the response to the top level page request, so that the browser does not have to wait for the Hoplon app to be initialized and then take control of the DOM to load the script.

It would be great if Hoplon supported this. (Often 3rd-party script doesn't have to integrate with the Hoplon app at all, in case that makes it easier for Hoplon to at least support that case.)

(when prerendering? ...) theoretically enables the Google Analytics snippet to be included in the precise way it's intended to be:

(head
  (when prerendering?
    (script :async "" :src "https://www.google-analytics.com/analytics.js")
    (script "window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;ga('create','UA-XXX','auto');ga('send','pageview');")))

However, the prerenderer currently ignores (script) elements, so as far as I know, there's no way to do this. 😢

Is it possible to fix this? This is such a common requirement, it'd be awesome if Hoplon had a good answer.

Thanks for your consideration, and thanks for the great work on hoplon!

@onetom
Copy link
Member

onetom commented Sep 24, 2015

I haven't looked into this "prerenderer currently ignores (script) elements" claim yet, but your snippet is buggy, because it only returns the 2nd script element. To return both, u should return them in a vector:

(head
  (when prerendering?
     [(script ...)
      (script ...)]))

The brackets are easy to miss in such situation, so we started to use

  (when prerendering?
     (vector (script ...)
             (script ...))))

It makes the structure more obvious but doesn't read very well, because vector is outside of the DOM terminology.
I guess for this very reason there is an alias called (spliced ...) for it Hoplon which communicates the intent a lot better than vector:

  (when prerendering?
     (spliced (script ...)
              (script ...))))

@jab
Copy link
Contributor Author

jab commented Sep 24, 2015

I haven't looked into this "prerenderer currently ignores (script) elements" claim yet

Please let me know what you find! I assumed it was true based on what I was seeing and @micha's doubts in Slack the other day:

micha [9/22 2:34 PM]: although prerendering scripts is tricky
the prerenderer may strip those out

Thanks for the tip about spliced. Just tried adding that, but alas, the script tags I'm trying to add appear neither in the prerendered html boot prod is generating nor in the dynamic html post-hoplonification.

@jab
Copy link
Contributor Author

jab commented Sep 25, 2015

And if this is in fact not possible yet, curious if @alandipert and @micha would consider this a priority for the 6.0.0 final release. It seems like it'd be a pretty important thing to be able to control, but maybe I'm misunderstanding something.

@alandipert
Copy link
Contributor

@jab I agree that it's important to be able to do. I just added Google Analytics to hoplon.io which uses prerendering.

I got ga.js in the head by writing a small boot task that you can see in use here: https://github.com/tailrecursion/hoplon.io/blob/master/build.boot#L81

Basically, it does a naive string replacement, and jams whatever js files you aim it at into the head of some target html.

It seems OK to me to do this out of band, because scripts like GA aren't ones you're interoperating with in the actual Hoplon application.

Would adding the inject task to boot-hoplon meet your needs? I'm game for a PR adding it, otherwise I'll get around to it eventually.

@jab
Copy link
Contributor Author

jab commented Sep 26, 2015

Thanks @alandipert, just gave your inject task a quick try and it looks like it'll do the trick! I'd love to take a crack at a boot-hoplon PR with it as soon as I can if that would help, though it'd be mostly copy/pasting your code so feel free to beat me to it / don't let me hold things up!

In the meantime, I'd be happy to add to the Hoplon wiki to document any takeaways from this thread if that'd help. But first just want to address the outstanding question, is it intentional (or at least a known/accepted limitation) that (script) doesn't work inside (when prerendering?)?

If so, I'll document that if it'd help, or open a new issue for fixing it otherwise.

Thanks again!

@onetom
Copy link
Member

onetom commented Oct 1, 2015

A related but different experimental solution form @micha: hoplon/boot-hoplon@d20a471 which uses a (script :prerender-keep "keep" "<some js script>") approach.

According the a Slack conversation between @micha & @alandipert (which I can't link because http://clojurians-log.mantike.pro/hoplon/index.html doesn't have an entry for 2015-10-01) the inject task seems to be a better approach.

@thedavidmeister
Copy link
Contributor

Hi, I just had an issue with a 3rd party script that does something to the page (adds some CSS) as soon as it is loaded into the page. If I use cljsjs then Hoplon blows away the changes it makes to the page (as Hoplon will destroy the changes when it builds the DOM).

If I stick the script in the Hoplon rendered DOM, and use their CDN, I run into extra page requests, etc. because the script is no longer optimised with everything else.

The 3rd party library I'm using is Lock by auth0 https://github.com/auth0/lock

@thedavidmeister
Copy link
Contributor

Hi, I just had the opposite problem to jab.

I wanted to bring in GA and a font after the page load, as it was adding ~400ms to page loads and having it so early isn't mission critical for me atm (I'd rather get the page in front of users asap).

I ended up doing something like this (using the standard GA snippet instead of cljsjs):

    (cell= (if in/window-loaded?
            [(google-analytics)
             (google-fonts)]))))

But then I ran afoul of the minifier confusing ga with something else (god knows what) when I turned it on.

I can manually rename ga to something longer and less likely to conflict with the minifier, but I wanted to share my experience here.

@mynomoto
Copy link
Contributor

For what I understood the problem that generated this issue has a solution so I'm closing this. Please reopen or open other issues if someone thinks this was mistakenly closed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants