Reputation: 48
I have the following pseudo-SPA like structure:
HTML layouts example
main.html
<html>
<!--main content above...-->
<!--dynamic content to replace following div...-->
<div id="target"></div>
<button onclick="replaceHtml('/sample/fragmentA')">fragmentA</button>
<button onclick="replaceHtml('/sample/fragmentB')">fragmentB</button>
<button onclick="replaceHtml('/sample/fragmentC')">fragmentC</button>
</html>
<script>
function replaceHtml(url){
$.ajax({
url:url
, success: function(data){
$("#target").html(data);
}
})
}
</script>
fragmentA.html
<div>
<!--some content to replace target...-->
</div>
<script>
var a = "a";
function somefunction_a(){
//do something in a...
}
</script>
fragmentB.html
<div>
<!--some content to replace target...-->
</div>
<script>
var b = "b";
function somefunction_b(){
//do something in b...
}
</script>
Requirements
fragmentsX.html
are dynamic pages that needs to be rendered by the server e.g. execute service, access DB etcfragmentsX.html
have <script>
that has variables and functions specific to that fragment.fragmentsX.html
and more could be added in the futuremain.html
as the base page and load dynamic fragments into it as opposed to loading individual fragmentsX.html
that include portion of the main.htmlQuestion
main.html
is loaded, when I access fragmentsA.html
then move onto fragmentsB.html
, I can still access all the variables and functions from the previous fragments (I'm assuming because they get loaded into memory once executed by jquery's html()
and are not released since it's the same "page")main.html
(which they will), wouldn't the client-side be flooded with variables and functions that are possibly no longer used but are not released from the memory? - maybe slowing down client-side rendering or allowing memory leak in return?html()
from memory before a new fragment is loaded?(while keeping vars and functions in main.html
)only solution I can come up with is creating a wrapper object at main.html
like so: var fragmentsVars = {}
and attach variables from fragments to this fragmentsVars.a = "a"
and emptying this variable after ajax complete: ...,complete(){fragmentsVars = {}}
but this solution would require all the fragment page developers to conform to this rule of creating vars and functions by appending to fragmentsVars
and I don't think this is practically possible in the long run (human error).
FYI, I'm using JS, thymeleaf for front-end and Java, Spring Boot for back-end
I don't think I can use thymeleaf's fragments
since all the fragments are dynamically loaded from user action and must be rendered server-side
Upvotes: 0
Views: 91
Reputation: 19301
To begin here's a test page to get a handle on some of the issues (modify as desired and test in a browser to get details of document.scripts
).
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>$fragTest</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
</head>
<body>
Target:
<div id="target"></div>
<hr>
<button type="button" onclick="loadFragA()">frag a</button><button type="button" onclick="loadFragB()">frag b</button>
<button type="button" onclick="test()">test</button>
<script text/javascript>"use strict";
function loadFragA(){
let html = 'FragmentA\n<script>\nvar a = "a";\nfunction something_a() {\n alert("something_a()");\n}\n<\/script>\n';
$("#target").html(html);
}
function loadFragB(){
let html = 'FragmentB\n<script>\nvar a = "b";\nfunction something_b() {\n alert("something_b()");\n}\n<\/script>\n';
$("#target").html(html);
}
function test() {
console.log( "typeof a: %s, a: '%s': typeof something_a: %s, typeof something_b: %s, doc.scripts: ",
typeof a, typeof a == "undefined" ? "undefined" : a,
typeof b, typeof b == "undefined" ? "undefined" : b,
typeof something_a, typeof something_b, document.scripts)
}
</script></body></html>
Running the test immediately demonstrates that the functions and global variables remain in place. If redeclared (like var a = 'b';
in fragmentB
) the new declaration replaces the old.
let
or const
or class
in fragments because reloading the fragment will attempt to redefine the identifier after the keyword which is not permitted.Global variables are global. main.html
should define what individual variables or a name space object that are reserved for inter-fragment communication, and provide a set of API endpoints that main
provides for fragment use.
Using the test code above showed that the number of script elements in the DOM did not go up as jQuery loaded new fragments or reloaded one previously loaded (in version 3.6.0 at least). Without looking into what jQuery is doing, to me this means you don't have to take care of removing script elements declared in fragments.
Regarding the choice between using an anonymous immediately executed function to perform set up in a fragment, the alternative would be to use the same setup function name in each fragment and call it after loading the fragment. During fragment load, the new version of the named setup function replaces the old function because JavaScript allows functions to be re-declared. Any function scope variables in the old setup function object will get garbage collected from memory.
If you implement a named setup function called from main.html
, then you might as well implement a corresponding destroy function that can be called immediately before fragment replacement.
main.html
APIThis outline should be read as details of a concept, not a directive to adopt the concept or its rules without further research or appropriate consideration:
main.html
exposes whatever API methods or data it provides in global scope, such as replaceHTML
and possibly a variable named destroyFragment
;main.html
also provides a namespace object for inter fragment communication called, say, fragmentNS
, which it does not monitor.setupFragment
(or similar) to be called after the fragment is loaded. This function contains all fragment code and variables.destroyFragment
(or similar) to get called for cleanup before the fragment is replaced.replaceHTML
gets expanded todestroyFragment
if it is a function,destroyFragment
to null,success
handler add a new line to call setupFragment
.Upvotes: 1