I am developing an Single Page Application (SPA) from scratch. I am doing it from scratch using only HTML, CSS and vanilla JavaScript and not using any external frameworks.
My application will initially load Web page but upon navigating to some other page say page2, it will only load required data and functions about other page2 from page2.js and not reload the entire Web page.
To use the JavaScript I will append it to body. But the problem is that when I navigate same page again it will append the same JavaScript again. The more pages I visit the more scripts are attached.
I have tried removing existing script tag in favour or upcoming script and it works good, but is there a way that I don't have to append script to DOM in the first place?
So my question is, is there a way we can parse (not just plain read) or execute JavaScript file without using any physical medium (DOM)
Although I am expecting pure JavaScript, libraries would also work, just need a logical explaination
So my question is, is there a way we can parse (not just plain read) or execute JavaScript file without using any physical medium (DOM)
Yes, you can. How you do it depends on how cutting-edge the environment you're going to support is (either natively, or via tools that can emulate some things in older environments).
In a modern environment...
...you could solve this with dynamic import, which is new in ES2020 (but already supported by up-to-date browsers, and emulated by tools like Webpack and Rollup.js). With dynamic import, you'd do something like this:
async function loadPage(moduleUrl) {
const mod = await import(moduleUrl);
mod.main();
}
No matter how many times it's requested, within a realm a module is only loaded once. (Your SPA will be within a realm, so that works.) So the code above will dynamically load the module's code the first time, but just give you back a reference to the already-loaded module the second, third, etc. times. main would be a function you export from the module that tells it you've come (back) to the "page". Your modules might look like this:
// ...code here that only runs once...
// ...perhaps it loads the markup via ajax...
export function main() {
// ...this function gets called very time the user go (back) to our "page"
}
Live example on CodeSandbox.
In older environments...
...two answers for you:
You could use eval...
You can read your code from your server as text using ajax, then evaluate it with eval. You will hear that "eval is evil" and that's not a bad high-level understanding for it. :-) The arguments against it are:
It requires parsing code; some people claim firing up a code parser is "slow" (for some definition of "slow).
It parses and evaluates arbitrary code from strings.
You can see why #2 in particular could be problematic: You have to trust the string you're evaluating. So never use eval on user-supplied content, for instance, in another user's session (User A could be trying to do something malicious with code you run in User B's session).
But in your case, you want and need both of those things, and you trust the source of the string (your server), so it's fine.
But you probably don't need to
I don't think you need that, though, even in older environments. Your code already knows what JavaScript file it needs to load for "page" X, right? So just see whether that code has already been loaded and don't load it again if it is. For instance:
function loadPage(scriptUrl, markupUrl) {
// ...
if (!document.querySelector(`script[src="${scriptUrl}"]`)) {
// ...not found, add a `script` tag for it...
} else {
// ...perhaps call a well-known function to run code that should run
// when you return to the "page"
}
// ...
}
Or if you don't want to use the DOM for it, have an object or Map or Set that you use to keep track of what you've already loaded.
Go back to old-school -- web 1.0, DOM level 1.0, has your back. Something like this would do the trick:
<html><head>
<script>
if (!document.getElementById('myScriptId')) {
document.write('<script id="myScriptId" src="/path/to/myscript"></scri' + 'pt>');
}
</script>
This technique gets everybody upset, but it works great to avoid the problems associated with doing dynamic loading via DOM script tag injection. The key is that this causes the document parser to block until the script has loaded, so you don't need to worry about onload/onready events, etc, etc.
One caveat, pull this trick near the start of your document, because you're going to cause the engine to do a partial DOM reparse and mess up speculative loading.
Related
I know document.write is considered bad practice; and I'm hoping to compile a list of reasons to submit to a 3rd party vendor as to why they shouldn't use document.write in implementations of their analytics code.
Please include your reason for claiming document.write as a bad practice below.
A few of the more serious problems:
document.write (henceforth DW) does not work in XHTML
DW does not directly modify the DOM, preventing further manipulation (trying to find evidence of this, but it's at best situational)
DW executed after the page has finished loading will overwrite the page, or write a new page, or not work
DW executes where encountered: it cannot inject at a given node point
DW is effectively writing serialised text which is not the way the DOM works conceptually, and is an easy way to create bugs (.innerHTML has the same problem)
Far better to use the safe and DOM friendly DOM manipulation methods
There's actually nothing wrong with document.write, per se. The problem is that it's really easy to misuse it. Grossly, even.
In terms of vendors supplying analytics code (like Google Analytics) it's actually the easiest way for them to distribute such snippets
It keeps the scripts small
They don't have to worry about overriding already established onload events or including the necessary abstraction to add onload events safely
It's extremely compatible
As long as you don't try to use it after the document has loaded, document.write is not inherently evil, in my humble opinion.
Another legitimate use of document.write comes from the HTML5 Boilerplate index.html example.
<!-- Grab Google CDN's jQuery, with a protocol relative URL; fall back to local if offline -->
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="js/libs/jquery-1.6.3.min.js"><\/script>')</script>
I've also seen the same technique for using the json2.js JSON parse/stringify polyfill (needed by IE7 and below).
<script>window.JSON || document.write('<script src="json2.js"><\/script>')</script>
It can block your page
document.write only works while the page is loading; If you call it after the page is done loading, it will overwrite the whole page.
This effectively means you have to call it from an inline script block - And that will prevent the browser from processing parts of the page that follow. Scripts and Images will not be downloaded until the writing block is finished.
Pro:
It's the easiest way to embed inline content from an external (to your host/domain) script.
You can overwrite the entire content in a frame/iframe. I used to use this technique a lot for menu/navigation pieces before more modern Ajax techniques were widely available (1998-2002).
Con:
It serializes the rendering engine to pause until said external script is loaded, which could take much longer than an internal script.
It is usually used in such a way that the script is placed within the content, which is considered bad-form.
Here's my twopence worth, in general you shouldn't use document.write for heavy lifting, but there is one instance where it is definitely useful:
http://www.quirksmode.org/blog/archives/2005/06/three_javascrip_1.html
I discovered this recently trying to create an AJAX slider gallery. I created two nested divs, and applied width/height and overflow: hidden to the outer <div> with JS. This was so that in the event that the browser had JS disabled, the div would float to accommodate the images in the gallery - some nice graceful degradation.
Thing is, as with the article above, this JS hijacking of the CSS didn't kick in until the page had loaded, causing a momentary flash as the div was loaded. So I needed to write a CSS rule, or include a sheet, as the page loaded.
Obviously, this won't work in XHTML, but since XHTML appears to be something of a dead duck (and renders as tag soup in IE) it might be worth re-evaluating your choice of DOCTYPE...
It overwrites content on the page which is the most obvious reason but I wouldn't call it "bad".
It just doesn't have much use unless you're creating an entire document using JavaScript in which case you may start with document.write.
Even so, you aren't really leveraging the DOM when you use document.write--you are just dumping a blob of text into the document so I'd say it's bad form.
It breaks pages using XML rendering (like XHTML pages).
Best: some browser switch back to HTML rendering and everything works fine.
Probable: some browser disable the document.write() function in XML rendering mode.
Worst: some browser will fire an XML error whenever using the document.write() function.
Off the top of my head:
document.write needs to be used in the page load or body load. So if you want to use the script in any other time to update your page content document.write is pretty much useless.
Technically document.write will only update HTML pages not XHTML/XML. IE seems to be pretty forgiving of this fact but other browsers will not be.
http://www.w3.org/MarkUp/2004/xhtml-faq#docwrite
Chrome may block document.write that inserts a script in certain cases. When this happens, it will display this warning in the console:
A Parser-blocking, cross-origin script, ..., is invoked via
document.write. This may be blocked by the browser if the device has
poor network connectivity.
References:
This article on developers.google.com goes into more detail.
https://www.chromestatus.com/feature/5718547946799104
Browser Violation
.write is considered a browser violation as it halts the parser from rendering the page. The parser receives the message that the document is being modified; hence, it gets blocked until JS has completed its process. Only at this time will the parser resume.
Performance
The biggest consequence of employing such a method is lowered performance. The browser will take longer to load page content. The adverse reaction on load time depends on what is being written to the document. You won't see much of a difference if you are adding a <p> tag to the DOM as opposed to passing an array of 50-some references to JavaScript libraries (something which I have seen in working code and resulted in an 11 second delay - of course, this also depends on your hardware).
All in all, it's best to steer clear of this method if you can help it.
For more info see Intervening against document.write()
I don't think using document.write is a bad practice at all. In simple words it is like a high voltage for inexperienced people. If you use it the wrong way, you get cooked. There are many developers who have used this and other dangerous methods at least once, and they never really dig into their failures. Instead, when something goes wrong, they just bail out, and use something safer. Those are the ones who make such statements about what is considered a "Bad Practice".
It's like formatting a hard drive, when you need to delete only a few files and then saying "formatting drive is a bad practice".
Based on analysis done by Google-Chrome Dev Tools' Lighthouse Audit,
For users on slow connections, external scripts dynamically injected via document.write() can delay page load by tens of seconds.
One can think of document.write() (and .innerHTML) as evaluating a source code string. This can be very handy for many applications. For example if you get HTML code as a string from some source, it is handy to just "evaluate" it.
In the context of Lisp, DOM manipulation would be like manipulating a list structure, e.g. create the list (orange) by doing:
(cons 'orange '())
And document.write() would be like evaluating a string, e.g. create a list by evaluating a source code string like this:
(eval-string "(cons 'orange '())")
Lisp also has the very useful ability to create code using list manipulation (like using the "DOM style" to create a JS parse tree). This means you can build up a list structure using the "DOM style", rather than the "string style", and then run that code, e.g. like this:
(eval '(cons 'orange '()))
If you implement coding tools, like simple live editors, it is very handy to have the ability to quickly evaluate a string, for example using document.write() or .innerHTML. Lisp is ideal in this sense, but you can do very cool stuff also in JS, and many people are doing that, like http://jsbin.com/
A simple reason why document.write is a bad practice is that you cannot come up with a scenario where you cannot find a better alternative.
Another reason is that you are dealing with strings instead of objects (it is very primitive).
It does only append to documents.
It has nothing of the beauty of for instance the MVC (Model-View-Controller) pattern.
It is a lot more powerful to present dynamic content with ajax+jQuery or angularJS.
The disadvantages of document.write mainly depends on these 3 factors:
a) Implementation
The document.write() is mostly used to write content to the screen as soon as that content is needed. This means it happens anywhere, either in a JavaScript file or inside a script tag within an HTML file. With the script tag being placed anywhere within such an HTML file, it is a bad idea to have document.write() statements inside script blocks that are intertwined with HTML inside a web page.
b) Rendering
Well designed code in general will take any dynamically generated content, store it in memory, keep manipulating it as it passes through the code before it finally gets spit out to the screen. So to reiterate the last point in the preceding section, rendering content in-place may render faster than other content that may be relied upon, but it may not be available to the other code that in turn requires the content to be rendered for processing. To solve this dilemma we need to get rid of the document.write() and implement it the right way.
c) Impossible Manipulation
Once it's written it's done and over with. We cannot go back to manipulate it without tapping into the DOM.
I think the biggest problem is that any elements written via document.write are added to the end of the page's elements. That's rarely the desired effect with modern page layouts and AJAX. (you have to keep in mind that the elements in the DOM are temporal, and when the script runs may affect its behavior).
It's much better to set a placeholder element on the page, and then manipulate it's innerHTML.
I'm not a full-time Javascript developer. We have a web app and one piece is to write out a small informational widget onto another domain. This literally is just a html table with some values written out into it. I have had to do this a couple of times over the past 8 years and I always end up doing it via a script that just document.write's out the table.
For example:
document.write('<table border="1"><tr><td>here is some content</td></tr></table>');
on theirdomain.com
<body>
....
<script src='http://ourdomain.com/arc/v1/api/inventory/1' type='text/javascript'></script>
.....
</body>
I always think this is a bit ugly but it works fine and we always have control over the content (or a trusted representative has control such as like your current inventory or something). So another project like this came up and I coded it up in like 5 minutes using document.write. Somebody else thinks this is just too ugly but I don't see what the problem is. Re the widget aspect, I have also done iframe and jsonp implementations but iframe tends not to play well with other site's css and jsonp tends to just be too much. Is there a some security element I'm missing? Or is what I'm doing ok? What would be the strongest argument against using this technique? Is there a best practice I don't get?
To be honest, I don't really see a problem. Yes, document.write is very old-school, but it is simple and universally supported; you can depend on it working the same in every browser.
For your application (writing out a HTML table with some data), I don't think a more complex solution is necessary if you're willing to assume a few small risks. Dealing with DOM mutation that works correctly across browsers is not an easy thing to get right if you're not using jQuery (et al).
The risks of document.write:
Your script must be loaded synchronously. This means a normal inline script tag (like you're already using). However, if someone gets clever and adds the async or defer attributes to your script tag (or does something fancy like appending a dynamically created script element to the head), your script will be loaded asynchronously.
This means that when your script eventually loads and calls write, the main document may have already finished loading and the document is "closed". Calling write on a closed document implicitly calls open, which completely clears the DOM – it's esentially the same as wiping the page clean and starting from scratch. You don't want that.
Because your script is loaded synchronously, you put third-party pages at the mercy of your server. If your server goes down or gets overloaded and responds slowly, every page that contain your script tag cannot finish loading until your server does respond or the browser times out the request.
The people who put your widget on their website will not be happy.
If you're confident in your uptime, then there's really no reason to change what you're doing.
The alternative is to load your script asynchronously and insert your table into the correct spot in the DOM. This means third parties would have to both insert a script snippet (either <script async src="..."> or use the dynamic script tag insertion trick. They would also need to carve out a special <div id="tablegoeshere"> for you to put your table into.
Using document.write() after loading the entire DOM do not allow you to access DOM any further.
See Why do I need to use document.write instead of DOM manipulation methods?.
You are in that case putting away a very powerfull functionnality of in web page...
Is there a some security element I'm missing?
The security risk is for them in that theirdomain.com trusting your domain's script code to not do anthing malicous. Your client script will run in the context of their domain and can do what it likes such as stealing cookies or embedding a key logger (not that you would do that of course). As long as they trust you, that is fine.
Assuming a JavaScript-based single-page application is returned by the server on the initial request. Besides some initialization code, which is common for every application, just the portion of the application needed to show the requested page (e.g. index page) is returned by the server on the initial request (then cached and rendered).
As the user clicks through the application, other portions of the application should be asynchronously loaded ('fetched', 'requested' or however you wanna call it) from the server. By "portions" a mean javascript code, images, css, etc. required to render the page. Let's focus on the javascript code part in this discussion.
It's important to notice that the javascript code to be returned to the browser is not contained in separate (static) files (which would be easy then and might be the case in the future due to e.g. performance reasons), but rather in one file, so it's not a 1:1 assiociation (request : file).
E.g. we could have a single app defined like this:
var LayoutPresenter = App.Presenter.extend('LayoutPresenter', {
__view: '<div>{{link "Author" "/author"}} - {{link "Book" "/book"}}</div>'
});
var AuthorPresenter = App.Presenter.extend('AuthorPresenter', {
__view: '<div><h1>{{name}}</h1></div>',
__parent: LayoutPresenter,
__context: { name: "Steve" }
});
var BookPresenter = App.Presenter.extend('BookPresenter', {
__view: '<div><h1>{{title}}</h1></div>',
__parent: LayoutPresenter,
__context: { title: "Advanced JavaScript" }
});
App.Presenter is part of the library I am writing and is available in the browser (or on any other client).
So assuming the user is browsing to the Book page which hasn't be loaded before (neither initially nor cached in the browser), the BookPresenter code, which is a function, should be returned by the server (assuming the LayoutPresenter code is already available in the browser and App.Presenter is available anyway because it's part of the library). I am running node.js on the server side.
How would you recommend to address this problem?
There is the eval function, so one could send javascript as a string and bring it back to live using eval(), but such an approach seems to be bad practice.
Never use eval - it's evil. The better option would be use jQuery ajax and set the dataType as script. This will evaluate your js, and also provide you with a call back once the script is loaded.
Refer to Ajax dataTypes and jQuery getScript shorthand. This is of course assuming that you can separate your code into logical modules
You might also consider it worth your time to check this question (How can I share code between Node.js and the browser?)
dNode is an option that is described in the question above and it looks quite exciting in terms of possibilities. You could create a list of function required for the Book page, then call them right off the server itself. That would eliminate the need to maintain separate js modules for each section of your page. Kudos to #Caolan for suggesting it.
As interesting as it is, take care to properly scope your functions; you don't want random users playing around on your server.
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..
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.