what is google map's javascript load strategy - javascript

When I use google maps, I am interested in its implemention, so I use the firebug to inspect.
Then I found that its javascript loading strategy is rather interesting. Take this page for example:
The overlay example
Then when I open this page first time, the following js are loaded:
https://maps.googleapis.com/maps/api/js?sensor=false
https://maps.gstatic.com/intl/en_us/mapfiles/api-3/9/13b/main.js
https://maps.gstatic.com/cat_js/intl/en_us/mapfiles/api-3/9/13b/%7Bcommon,map,util,poly%7D.js
https://maps.gstatic.com/cat_js/intl/en_us/mapfiles/api-3/9/13b/%7Bonion,geometry%7D.js
But if I refresh the page(use the ctrl+f5), the following js are loaded:
https://maps.googleapis.com/maps/api/js?sensor=false
https://maps.gstatic.com/intl/en_us/mapfiles/api-3/9/13b/main.js
However the page still works, the overlay is drawn in the map. But where is the poly.js and etc?
Also, can anyone tell me how to load the js by components? For exmaple the common util poly in the example.
What should I know when I write the different components?

1. When poly.js loads, it passes a string to google.maps.__gjsload___.
Here's an excerpt:
google.maps.__gjsload__('common', '\'use strict\';var Ai=isNa...
The rest of the file is just the contents of that string.
My hunch is this function probably stores this string in localStorage or sessionStorage so that it only has to be retrieved once.
2. Also, if you want to learn about loading js files as-needed, look into AMD and/or CommonJS:Modules.
A good imlementation of AMD (my preference) is RequireJS.
Update
I did some poking around, and localStorage and sessionStorage do not appear to be being used on this page. I also can't duplicate your results. In Firebug, poly.js always loads for me. There may be some magic happening somewhere, but I don't see it.
However, it's entirely possible to store a string in localStorage and sessionStorage for retrieval without having to make an extra js call.

Also,any one can tell me how to load the js by components?
this touches on the topic of asynchronous javascript file loading. if you've ever used a language that has a way to "include" a file at any point in a script, you'll understand that javascript does not have this capability. because of that, there is this whole paradigm of "aysnc javascript addition" via script tag injection.
script tag injection: you dynamically make a script tag, and set its source to the file you need, and insert that tag into the DOM, and voila, a new file has been loaded and executed. With javascript heavy applications, this is common, especially when loading third party applications. Google does it alllll the time, just check out google analytics' include script for a good example of this.
Now, since this is a touchy and delicate type of coding to do, some "javascript component / module / asset loading" frameworks have refined it and made it pretty stable. common.js, require.js, etc have all done good jobs at this.
What should I know when I write the different components ?
For what you're doing with google maps, you don't really need to know much. but if you get into javascript module pattern development, you need to know this: make sure you protect your global namespace from being cluttered by your own variables, so encapsulate all of your work in closures when possible, and (recommended but not required) start them all with a ; so they don't break each other if they get loaded out of order.

Related

Why do websites like Hotjar and Google Analytics use complex tracking code instead of just a <script> tag?

Website that use JS tracking usually use this kind of code :
<script>
(function(h,o,t,j,a,r){
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
h._hjSettings={hjid:9999,hjsv:5};
a=o.getElementsByTagName('head')[0];
r=o.createElement('script');r.async=1;
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
a.appendChild(r);
})(window,document,'//static.hotjar.com/c/hotjar-','.js?sv=');
</script>
In the end, those scripts just add a <script> tag to the <head> of the page, so surely there must be a reason why they're doing it this way.
Is it for ad-blocking bypass reasons ? Wouldn't the generated request be the same as if it was hardcoded in the <head> ?
I'm the chief architect at Hotjar so I'll explain the reasons why we did it in this particular way.
We need to do things before the main script is loaded.
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
That particular line allows us to store actions to execute once the main script is loaded. It allows for things like hj('trackVirtualPageView', '/url') to be called before our script is loaded.
We can store things like settings as part of the snippet.
h._hjSettings={hjid:9999,hjsv:5};
That could absolutely be added as part of the query string when loading the script. The downside of using that approach is that we would get less optimal caching since it would be impossible for a browser to know that script.js?hjid=1 and script.js?hjid=2 actually loads the same JS file.
What we're doing in the last part is actually just creating a <script async=1> tag and adding it to the <head> which works really well. The reason we're doing it through JS is that we like to make it as easy as possible for our users by only asking them to put code in one place.
There might be an even better to do what we're doing which I'm blissfully unaware of, and in case there is, please reach out and tell me about it! :)
At least part of the answer is that vendors want to load their libraries in a way that does not block page rendering.
If the browser hits a script element it tries to get the script source, and might prevent the page from rendering until the complete script is downloaded. In the bad old days it used to happen that website would show up blank, because the (then synchronous) Google Analytics script could not be downloaded in a timely fashion and stopped the page from rendering. Script injection became an accepted method to make scripts non-blocking.
There are other ways (defer, asynch, etc - for historical interest here is a link to an 2009 article that discusses the issue, because the problem is that old), but script injection is a convenient way to set up a few variables along the way (plus if Google does it it must be the best way, or so seems to be the though process with some companies).

Is there a danger to creating a js file that simply uses document.write() to bundle js and css files?

I am building a front-end UI framework for developers in my firm to use to build internal web apps. It consists of customized Bootstrap, jQuery, other open-source libraries, internal modules and stylesheets. The user environment is entirely IE9 and my server is .NET 3.5. I am hosting the shared files. Dev teams in the firm will place the links in their project pages and apply the framework to their pages.
I want to offer them the simplest method of implementing this which would be one line of code to paste that builds the library for them. Cutting and pasting 30 lines of code is stale the moment Ctrl + V is pressed, it leaves me no control and is simply inelegant.
Failed Experiments
I tried using Head.js and LazyLoad both of which use best practices for inserting scripts. But each of them has caused either content to display before styled or conditions where methods are called before scripts load. I am giving up on this approach.
It's too volatile.
A simple document.write() solution
Over the weekend, I thought: Why don't I just make a js file named "framework,js", add the script and link files in order with a stack of document.write() lines. Tell developers to put it in the head and that's it. Heck I could add the necessary metatags for IE9 and mobile too for that matter. It's so nasty and simple... but it just might work!
The user base is on an internal network and of limited size. Bandwidth is not a problem. I'll test performance before I choose this. I can direct the developer teams on where to place the link.
Knowing this and providing it actually works, is there any reason why I shouldn't do this?
My only other option to explore is bundling on the server. I am hoping not to have to resort to this since I don't own the server myself and I am not a .NET developer.
Your proposed approach is an excellent one for your situation. It has several advantages over the more sophisticated solutions, including utter simplicity, completely predictable order of execution, and full compatibility with scripts that may not lend themselves to asynchronous loading. I have used it many times in production applications, as have many other developers.
Of course the other solutions have their advantages too, but for what you're doing there is nothing wrong with good old document.write().
It sounds like you have a number of scripts and stylesheets and are probably loading them from a common directory (or a common root directory). To reduce repetition in your framework.js file, you might want to define two functions, one to write a <link> tag for CSS and another to write a <script> tag for JavaScript. So a skeleton framework.js file might look something like this:
(function() {
var scriptBase = 'js/';
var styleBase = 'css/';
function writeStyle( name ) {
document.write(
'<link rel="stylesheet" href="', styleBase, name, '">',
'</link>'
);
}
function writeScript( name ) {
document.write(
'<script src="', scriptBase, name, '">',
'</script>'
);
}
writeStyle( 'one.css' );
writeStyle( 'two.css' );
writeScript( 'one.js' );
writeScript( 'two.js' );
})();
Note that you don't have to do any special escaping of the </script> text as you may see in code that uses document.write(). That's only necessary when you're putting this code directly inside a <script> tag within the HTML file. The purpose of that escaping is to prevent the enclosing <script> tag from being closed by the </script> text inside the document.write() call. Since your code is in an external .js file this is not an issue: the presence of the </script> text inside this file won't terminate the .js file.
Another point to keep in mind is that all of the document.write() calls you make inside a .js file are inserted into the document after the .js file that writes them. So don't expect to be able to do a document.write() inside framework.js and then have other code inside framework.js that depends on the .js file you just wrote. All of those .js files (and .css) are loaded after framework.js, not interleaved with it.
One more consideration, of course, is load time. Your page could load faster if you combine and minify your CSS and JS files. But if these are internal web apps, page load time may be the least of your worries: reliability and maintainability may be more important. And in any case you can always use the document.write() solution to get up and running right now, and later optimize this only in the unlikely event that you need to.

How to use javascript in Silverstripe CMS?

I'm using SilverStripe 3.0 CMS, and I need to include a Google Map into the CMS.
I'm following this steps, and besides it's a little bit old, the official documentation uses the same methods in the current version of SilverStripe (At least it seems to be the current version documentation).
The issue is in this part of the code:
Behaviour.register({
"#Form_EditForm" : {
initialize : function() {
this.observeMethod("PageLoaded", this.adminPageHandler);
this.adminPageHandler();
},
adminPageHandler : function() {
initialize();
}
}
});
First of all, Behaviour was not defined. I needed to include manually the behaviour.js file that comes within the framework. But now, I get a Type Error:
this.observeMethod is not a function
Can someone give me a hint of what can I do in order to call a javascript function when a page editor is opened in the SilverStripe CMS?
the 'Behaviour.register' call you mention is definitly deprecated and no longer available in the core code, so the docs need an update here.
unfortunately, i couldn't find a documented way to replace this behaviour, but for now the following should work for you, based on the approach in the forum post you mentioned first hand:
find the 'initGoogleMaps.js' script added here:
function getCMSFields() {
Requirements::javascript('mysite/javascript/initGoogleMaps.js');
...
inside this script, remove the Behaviour.register... block, and move the initialize function outside document.ready (or simply remove the document.ready part), so initialize is globally available (you might consider renaming it).
then, add the following inside getCMSFields:
$fields->addFieldToTab('Root.Content', new LiteralField('js', '<script>initialize();</script>'));
this will ensure the initialize function is called every time a page's 'edit view' is rendered inside the cms.
hth
As mentioned by ben,
LeftAndMain::require_javascript('mysite/javascript/initGoogleMaps.js')
is more reliable than 'include-it when needed'. Why?
Because Silverstripe uses Ajax, it is better to load any javascript or css on the first load, so that they are ready when you go to different model admin areas within the CMS in ajax-powered environment. Not loading at the start causes inconsistency and your js, css files will not be loaded when you don't hard-load that admin area.
From documentation: http://doc.silverstripe.org/framework/en/reference/requirements and http://api.silverstripe.org/3.0/class-LeftAndMain.html
The whole "include it when you need it" thing shows some weaknesses in
areas such as the CMS, where Ajax is used to load in large pieces of
the application, which potentially require more CSS and JavaScript to
be included. At this stage, the only workaround is to ensure that
everything you might need is included on the first page-load.
One idea is to mention the CSS and JavaScript which should be included
in the header of the Ajax response, so that the client can load up
those scripts and stylesheets upon completion of the Ajax request.
This could be coded quite cleanly, but for best results we'd want to
extend prototype.js with our own changes to their Ajax system, so that
every script had consistent support for this.
By the way, the ideal place for this line is _config.php in your custom module or in mysite, depending on your needs.
LeftAndMain::require_javascript('mysite/javascript/initGoogleMaps.js')
would work much better

Page-level execution of JavaScript when serving concatenated files

Scenario:
A web site with x number of pages is being served with a single, concatenated JavaScript file. Some of the individual JavaScript files pertain to a page, others to plugins/extensions etc.
When a page is served, the entire set of JavaScript is executed (as execution is performed when loaded). Unfortunately, only a sub-section of the JavaScript pertains directly to the page. The rest is relevant to other pages on the site, and may have potential side-effects on the current page if written poorly.
Question:
What is the best strategy to only execute JavaScript that relates directly to the page, while maintaining a single concatenated file?
Current solution that doesn't feel right:
JavaScript related to a specific page is wrapped in a "namespaced" init function for that page. Each page is rendered with an inline script calling the init function for that page. It works hunky-dory, but I would rather not have any inline scripts.
Does anyone have any clever suggestions? Should I just use an inline script and be done with it? I'm surprised this isn't more of an issue for most developers out there.
Just use an inline script. If it's one or two lines to initialize the JavaScript you need that's fine. It's actually a good design practice because then it allows re-use of your JavaScript across multiple pages.
The advantages of a single (or at least few) concatenated js files are clear (less connections in the page mean lower loading time, you can minify it all at once, ...).
We use such a solution, but: we allow different pages to get different set of concatenated files - though I'm sure there exists different patterns.
In our case we have split javascript files in a few groups by functionality; each page can specify which ones they need. The framework will then deliver the concatenated file with consistent naming and versioning, so that caching works very well on the browser level.
We use django and a home-baked solution - but that's just because we started already a few years ago, when only django-compress was available, and django compress isn't available any more. The django-pipeline successor seems good, but you can find alternatives on djangopackages/asset-managers.
On different frameworks of course you'll find some equivalent packages. Without a framework, this solution is probably unachievable ;-)
By the way, using these patterns you can also compress your js files (statically, or even dynamically if you have a good caching policy)
I don't think your solution is that bad although it is a good thing that you distrust inline scripts. But you have to find out on what page you are somehow so calling the appropriate init function on each page makes sense. You can also call the init function based on some other factors:
The page URL
The page title
A class set in the document body
A parameter appended to your script URL and parsed by the global document ready function.
I simply call a bunch of init functions when the document is ready. Each checks to see if it's needed on the page, if not, simply RETURN.
You could do something as simple as:
var locationPath = window.location.pathname;
var locationPage = locationPath.substring(locationPath.lastIndexOf('/') + 1);
switch(locationPage) {
case 'index.html':
// do stuff
break;
case 'contact.html':
// do stuff
break;
}
I'm really confused exactly why it doesn't feel right to call javascript from the page? There is a connection between the page and the javascript, and making that explicit should make your code easier to understand, debug, and more organized. I'm sure you could try and use some auto wiring convention but I don't think it really would help you solve the problem. Just call the name spaced function from your page and be done with it..

Putting JavaScript at the end of the page produces an error

I recently read that for a faster web page load it's a good practice to put the JavaScript links at the end. I did, but now the functions of the referenced file doesn't work. If I put the link at the beginning of the page, everything is fine.
Does this thing of putting JavaScript at the end work only under certain circumstances?
I went through some testing with this as well. If you are loading a Javascript file it is faster to put it at the end BUT it does come with some important caveats.
The first is that doing this often made some of my visual effects noticeable. For example, if I was using jQuery to format a table, the table would come up unformatted and then the code would run to reformat it. I didn't find this to be a good user experience and would rather the page came up complete.
Secondly, putting it at the end made it hard to put code in your pages because often functions didn't exist yet. If you have this in your page:
$(function() {
// ...
});
Well that won't work until the jQuery object is defined. If it's defined at the end of your page the above will produce an error.
Now you could argue that all that styling code could be put in the external file but that would be a mistake for performance reasons. I started off doing that on a project and then found my page took a second to run through all the Javascript that had been centralized. So I created functions for the relevant behaviour and then called the appropriate ones in each page, reducing the Javascript load run time to 50-200ms.
Lastly, you can (and should) minimize the load time of Javascript by versioning your Javascript files and then using far-futures Expires headers so they're only loaded once (each time they're changed), at which point where they are in the file is largely irrelevant.
So all in all I found putting putting the Javascript files at the end of the file to be cumbersome and ultimately unnecessary.
You do have to pay attention to the ordering, but libraries like JQuery make it easy to do it right. At the end of the page, include all the .JS files you need, and then, either in a separate file or in the page itself, put the Jquery calls to act on the page contents.
Because JQ deals with css-style selectors, it's very easy to avoid any Javascript in the main body of the page - instead you attach them to IDs and classes.
This is called Unobtrusive Javascript
Every Yahoo YUI example file I remember has almost all the JavaScript at the end. For example,
Simple Event Handling
Basic Drag and Drop
JSON: Adding New Object Members During Parsing
It looks like Yahoo Practice is roughly "library code at the beginning of <body>, active code at the end of <body>."
Beware, though, this may result in the Flash of Unstyled Content syndrome.

Categories