Ankur Parihar
Ankur Parihar

Reputation: 495

How to use javascript without appending it to DOM

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

Upvotes: 2

Views: 1336

Answers (2)

Wes
Wes

Reputation: 1090

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.

Upvotes: 2

T.J. Crowder
T.J. Crowder

Reputation: 1074355

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:

  1. It requires parsing code; some people claim firing up a code parser is "slow" (for some definition of "slow).

  2. 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.

Upvotes: 5

Related Questions