I'm creating a site where newly opened pages are loaded via AJAX. Each page uses a global.js and a .js just for that page. The problem is that when I load a new page the JavaScript of the previous page will still remain no matter what.
My AJAX:
$.post({
url: "./some_url",
dataType: 'html',
data: { ajax: true },
success: function(data) {
//Here I would need a way to stop all the previous JavaScript
$("#site").empty().append(data);
}
});
Now I am looking for the best solution to this situation, because in some situations a page will have javascript running for x amount of time (by this I mean that a function will be looping doing some stuff) so I will have to add some if statement to check am I still on the correct page.
So the question is how should I solve it, is there a way to unload some javascript or maybe I should wrap all my functions into an object and null that object?
Also will the removed html elements EventListeners added via javascript be removed or will they actually stay somewhere until I manually remove them?
well AJAX is for dealing with requests in background, just for not loading all page again so... your logic seems to be invalid.
Anyway you can delete scripts in-page or links to js sources:
<script id="mustBeRemoved">....</script>
or
Then with jQuery:
$("#mustBeRemoved").remove();
Or with plain JS:
element.parentNode.removeChild(document.getElementById("mustBeRemoved"));
The fact now is when deleting it?
Just delete it when you don't need it anymore (before the AJAX request, for example, or just at the response, before loading something else).
EDIT:
Once a script is loaded, the objects and functions it defines are kept in memory. Removing a script element does not remove the objects it defines. This is in contrast to CSS files, where removing the element does remove the styles it defines. That's because the new styles can easily be reflowed.
However, if you have a file that defines "myFunction", then you add another script that redefines myFunction to something else, the new value will be kept. You can remove the old scripttag if you want to keep the DOM clean, but that's all removing it does.
The only real way to "clean up" functions that I can think of is to have a JS file that basically calls delete window.myFunction for every possible object and function your other script files may define. For obvious reasons, this is a really bad idea.
If you can analyse the js you need before coding it probably you can avoid this problems. If you project is enough developed for getting back, then the best way seems to delete the old script or link to the old script, and redeclare this functions to simply do nothing (or doing new different things).
Related
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.
I have a website that loads mostly using AJAX calls. The javascript and CSS files are only loaded once when the page first loads.
My issue is that the javascript/CSS can get out of sync with the HTML and server-side code. The page can be using an old versions of the javascript file (from when the page first loaded) while the server-side code and ajax-loaded HTML files always use the latest code and files.
What are some strategies for dealing with this?
I have considered polling the server at set intervals and asking if there is a newer version of the JS. Then, if there is, reloading the page. But, it seems that this can get ugly, with the page suddenly reloading at awkward moments instead of, for example, as the result of a user-initiated call.
Also, there are some changes to the javascript that do not necessarily require that a page be reloaded. For example, the changes might affect a different page/module than the one that the user is on.
Re-loading the javascript with every Ajax call is not viable
I can imagine ugly solutions to this, but thought I'd ask first.
EDIT (in response to comments and suggested answers)
The only way to get the JS back into sync is to reload the page, which then loads the new JS. Adding new JS to an old page won't work as it doesn't get rid of any old functions, listeners, etc. I'm not asking how to reload a page or how to load javascript. I'm asking for a strategy of knowing WHEN to do it, especially in a way that does not seem awkward to the user. Do people incorporate polling to ask if there is a new JS version? Do they then suddenly (from the user's point of view) reload the page? Do they poll even when the tab is hidden? Is this a problem for the server? Where do they keep track of the latest required JS version? Or, do they ask with every AJAX request - hey, should I reload? Did they write a special function for that? Do they keep all new html/server code backwards compatible with the js?
Someone who has dealt with this, how do you do it?
Two possible solutions include
calling $.getScript() to retrieve, update variables at document from at server-side to match variables at document before calling $.ajax(/* settings */) ;
alternatively could use web workers to update original document variables to match server-side variables at beforeSend of $.ajax(/* settings */)
At result of first step of either approach, abort $.ajax() call, call error handlers, notify user, send message to server about error.
var head = document.getElementsByTagName("head")[0],
scripts = {};
function load_script(name){
var myscript = document.createElement('script');
myscript.setAttribute("type","text/javascript");
myscript.setAttribute("src", name);
if (scripts[name]) head.replaceChild(myscript, scripts[name]);
else head.appendChild(myscript);
scripts[name] = myscript;
}
// the first call to load the script code
// and then call if you decide to upgrade a newer version
load_script('js1.js');
load_script('js2.js');
I have a relatively large (in terms of memory use and code size) JS function (running in IE/FF) that I use only occassionally (like, a couple of times per day). I think I can get rid of it when I'm done with it by nulling out its function (using the variable name of the 'function object', as it were).
I am fuzzy though on how I would get it back, supposing maybe some time later I wanted to do it again. How would I load JS on the fly from a URL like the 'script' tag does?
Does this whole line of reasoning make sense?
It's a tad hacky, but there are two ways:
Use DOM methods to insert a script tag into the page to a file that has that function in it. You might need to add a query string so that it thinks it's a new javascript file (like function.js?(random number))
Use AJAX to download the file with the function and eval(); it
The only real way to do this is to insert a script element into the document dynamically using JavaScript with a link to a script file containing your function, causing the script to be loaded. One caveat: you must make sure that the filename has the time appended as a query string, otherwise cache unfriendly browsers like Internet Explorer will not reload the script again.
Like others have said, the best bet is to go ahead and insert a new script tag into the page with some kind of query parameter to avoid caching issues. If you're using a JS Library, this technique is actually called "JSONP"; jQuery in particular has a nice method for doing this that gives you an easy way to attach a callback function and such. If you write your own native version, you'll need to watch the readystate of the new script node to know when it's actually loaded.
That said, one thing I'm curious about - and anyone else, please correct me if I'm wrong - why not use the "delete" keyword to kill your function, instead of nulling it out? Something like...
function myFunction() { return; }
Then...
delete myFunction;
Seems to be a more efficient way of cleaning things up, at least from my perspective.
I have a script element in my webpage, something like this:
<script id="myscript"></script>
Now, from a javascript file, I'm doing something like the following:
$('#myscript').src('http://foo.bar?callback=somefunc')
Now this remote script 'returns javascript' of the following form:
somefunc(somearg);
When I run all of this, things work neatly, the script gets loaded dynamically, and the 'somefunc' callback is executed.
The problem happens when I do the same thing again. Let's say I again call the same thing:
$('#myscript').src('http://foo.bar?callback=somefunc')
This, for some reason, DOESNT return the javascript call in Firefox only. (Works fine in IE - somefunc gets executed again as expected).
I can think of ugly workarounds (such as doing a $('head').append('<script...')) every time - but I'd like to know what's going on here.
Thanks in advance!
I would recommend you to use $.getScript instead of using a single script tag load scripts multiple times:
$.getScript("http://foo.bar?callback=somefunc");
That function will abstract the script element creation and its introduction to the DOM.
But it seems you are accessing a JSONP service, in that case you need only $.getJSON:
$.getJSON("http://foo.bar?callback=?", function(json){
// callback
});
I can think of ugly workarounds (such as doing a $('head').append('
Ugliness is subjective; personally, I find the technique you're trying to use (making a single script tag load multiple scripts) far uglier.
But that's not really important. Adding a new script tag works - so if you're having trouble with what you're doing, just use the normal method and live with it.
FWIW: Firefox probably doesn't respond because you're not actually changing anything... If you want to make this even uglier, append some do-nothing querystring parameter that changes each time through.
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.