Currently we have an extensive web-based office application written in PHP, javascript to manage various processes (billing, ordering, user management, reporting, subscription management, article managementm, warehousing etc.)
The application uses iframes to load pages that perform various functions within the main application interface. From the main page tabs are created so you can access all open pages if needed. Each page has their own javascript include-files, css etc to perform complex tasks. All iframed-pages (around 600) are on the same domain/host.
Since many people describe using iframes as 'evil', I was examining on how I could convert the application into using divs only.
The quesions I have are:
1) in a situation where there are no iframes anymore, do I need to include every used javascript files on the main start page? Or is it possible to dynamically load javascript files into the memory depending on which page you access? (inluding new function declarations etc.)
2) Can these include-files be removed completely out of the memory when someone closes a page? (in this case a div). This to avoid possible conflicts between function names & variables of different files and also not to burden the browser's memory usage.
3) Or perhaps iframes are not that 'evil' afterall, and this is a correct way for using iframes?
thanks for the advise
chees
Patrick
have you tried any code or just asking?
you can use jquery (see here)
$.getScript("ajax/test.js", function(data, textStatus, jqxhr) {
console.log(data); //data returned
console.log(textStatus); //success
console.log(jqxhr.status); //200
console.log('Load was performed.');
});
As long as your javascript is modular, you can unload it whenever you want.
In case you don't know, in Javascript functions are objects. And with objects you can do this:
var a = {b:10, c:20, d:30}; //Some memory is allocated for object a
a = null; //Object a will be deleted, and no longer need any memory
How can you use that to handle functions? Here's easiest explanation:
var myFunction = function(a,b,c) {
alert(a+b+c);
}; //Here we have a function stored in memory
myFunction = null; //And now we don't
You can make whole libraries of functions this way and then remove them with one operation. Example:
var MyMath = {};
MyMath.abs = function(a) {
return (a<0)?-a:a;
};
MyMath.min = function(a,b) {
return a>b?b:a;
}; //MyMath uses some memory.
alert(MyMath.min(5,10));
MyMath = null; //Now it does not
So, wrap all of your libraries into objects, and unload those objects whenever you need.
Added:
When your browser sees some new Javascript code - it executes it all at once. It also does save it in memory, but never uses it again (basically it is stored the same way hello is stored in <div>hello</div>). Therefore the best solution would be:
1) Add <script> tags dynamically to load desired Javascript files on demand. It will look something like:
if (MyMathLibrary == null || MyMathLibrary == undefined) {
createScriptElementForMathLibrary();
}
alert(MyMathLibrary.calculateEnthropyOfTheUniverse());
2) Whenever you no longer need the library, you just do MyMathLibrary = null. You can also delete it from DOM at this point, it won't do any harm, as I said, it is no more than invisible div at this point.
But honestly, are you sure this is worth the bother? People nowadays write 3D shooters purely in Javascript and computers come with 4-16 GB of memory. Also, it's common practice to first make a working program and bother with optimizations only if it lags. Otherwise it is likely that you will spend a month of work for something no user will ever notice.
Related
I support a very old PHP web framework that uses server-side rendering. I decided to implement Vue for the rendering of some modules, so I compiled a hello world app and realized deployment wouldn't be so simple.
The framework works as a giant SPA, with each module being rendered using the html output of a body() function. The output is replaced in the client's DOM without reloading the page itself.
<script> tags are banned for security reasons and will be sanitized from the resulting html. The only way to deliver JS to the client is by using an eval_js() function.
The problem is rather simple. I need to safely load JS code several times in the same DOM. I cannot load it as-is after app compilation, because from the second time onwards the code is executed (every time a user visits a module, or performs an action) the code will attempt to re-define global variables and kill the whole client.
The solution is also rather simple, just rewrite the JS code such that every global definition is transformed into a window property. This way, even if the same piece of code gets executed several times in the same DOM, it will simply replace window properties rather than attempting to re-define variables.
In example, the following input:
function Yr(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
var hOe = sg(uOe, fOe, dOe, !1, null, "e687eb20", null, null);
const vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: pOe
}
};
new Yr({...}).$mount("#app");
Would be rewritten into:
window.Yr = function(t){
const b = t.prototype.hasOwnProperty;
this._init(b);
}
window.hOe = sg(window.uOe, window.fOe, window.dOe, !1, null, "e687eb20", null, null);
window.vOe = {
name: "AmmFilters",
components: {
AmmOptionSelect: window.pOe
}
}
new window.Yr({...}).$mount("#app");
I initially considered to write my own parser, but then realized that ES6+ syntax is no child's play. The code I will attempt to rewrite is optimized & obfuscated which means it will have all sort of complex syntax and I must be careful not to turn scoped definitions into window properties.
Any ideas on a tool that already performs this task? The resulting JS code should have no difference from the original, as global scoped variables end up in the window object anyway.
I believe it would be a fairly useful tool for various use cases, so thought about asking before attempting to reinvent the wheel.
My understanding is that Monaco is optimized for editing, and for having showing one file at a time, with a fixed size editor that has its own scroll bar.
Instead I am trying to build one page with the diffs of multiple files below each other
allowing showing/hiding each file, up to ~100 files
hiding portions of the file that have not changed (and allowing to show them as context if desired)
not have one scrollbar per file, but one for the entire page
the files are usually view-only, but editing should be supported for one file at a time
I realize this is quite the departure from what Monaco was built for, but in the end it seems as if the same viewport and virtual rendering tricks would apply, so maybe it is somehow possible?
I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.
One pretty ugly workaround might be to have a single Monaco instance, concat all the files, and then work with ViewZones, custom line number providers and code folding providers to achieve the impression of multiple files. Is that as crazy as it sounds, or might that actually work?
Any other suggestions? Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?
Citate from your question:
I tried creating one Monaco instance per file, but that starts getting really sluggish around 30 instances.
The solution for your question
As you mentioned the perfomance was sluggish. This is because your server or may be your client has not enough memory. You have to add more memory to the server or may be to the client for more perfomance. Because I do not have enough information I can not say it is the server or the client. But this method is not efficient.
Citate from your question:
Why does IStandaloneDiffEditor have standalone in the name? Does that mean there is another way to create many diff editors that is more efficient?
Nothing from it. In Wikipedia I found the answer what standalone means:
Standalone software may refer to:
Computer software that can work offline, i.e. does not necessarily require network connection to function.
Software that is not a part of some bundled software.
A program that run as a separate computer process, not an add-on of an existing process.
Standalone program, a program that does not require operating system's services to run.
A portable application, which can be run without the need for installation procedure.
This means that standalone has nothing to do with single instance and you could have multiple instances of this editor. But you have to have more memory on your computer(s) to create 100 instances from this editor. And this it not efficient because you have 100 big JavaScript objects more in your memory.
On other services for display the difference between changed files they make it with DOM objects only or with DOM objects + one big instance from big JavaScript object which creates this objects, but not additional 100 big instances from big JavaScript objects.
Accordingly to this princip in this case you could use code from my recommended solution below and in background cleate only one instance from this difference editor. Then you have to put to this instance all your 100 files one after other and copy in each case from one file following DOM objects:
<div class="editor original showUnused" ...
<div class="editor modified showUnused" ...
This could you do for example with following code:
var diffPartContainars = document.querySelector('#container').querySelectorAll('.showUnused'),
editorOriginalPartHTML,
editorModifiedPartHTML;
for(var i = diffPartContainars.length; i--;)
{
var obj = diffPartContainars[i],
cln = obj.className;
if(cln.indexOf('editor original') > -1)
{
obj.removeAttribute('style');
editorOriginalPartHTML = obj.outerHTML;
}
if(cln.indexOf('editor modified') > -1)
{
obj.removeAttribute('style');
editorModifiedPartHTML = obj.outerHTML;
}
}
Then you have to delete from each editorOriginalPartHTML and editorModifiedPartHTML following DOM objects:
<div class="invisible scrollbar horizontal" ...
<canvas class="decorationsOverviewRuler" ...
<div class="visible scrollbar vertical" ...
and all other objects which you can not use. You can do it when you add editorOriginalPartHTML and editorModifiedPartHTML to your DOM. Then you can add around each from it one div object with suitable width, height and style="overflow:auto". And one thing you could do more: for each from this div objects you could add one onclick or onmouseover listener and then replace this div object view with your difference editor instance.
This is only one way in my opinion to be more efficient. Good luck!
The recommended efficient solution
The quickly, comfortable and efficient way to have only one instance of this editor and load a new sources on the click on the file names like below.
var diffEditor = null;
var filesContent =
{
'SomeJavaScriptFile.js':
{
originalContent: 'alert("heLLo world!")',
modifiedContent: 'alert("hello everyone!")',
type: 'text/javascript'
},
'AnotherJavaScriptFile.js':
{
originalContent: 'function open(str)\n{\n\talert(str)\n}',
modifiedContent: 'function output(value)\n{\n\tconsole.log(value)\n}',
type: 'text/javascript'
}
};
document.querySelector('#files').addEventListener('change', function(e)
{
var fileName = this.options[this.selectedIndex].text,
file = filesContent[fileName];
openInDiffEditor(file);
});
function openInDiffEditor(file)
{
if(!diffEditor)
diffEditor = monaco.editor.createDiffEditor(document.querySelector('#container'));
diffEditor.setModel({
original: monaco.editor.createModel(file.originalContent, file.type),
modified: monaco.editor.createModel(file.modifiedContent, file.type)
});
}
//open the first file in select list:
var firstFileName = document.querySelector('#files').options[0].text;
openInDiffEditor(filesContent[firstFileName]);
<p>Please select one file on the left list to see the file differences after changes.</p>
<select id="files" size="3">
<option selected>SomeJavaScriptFile.js</option>
<option>AnotherJavaScriptFile.js</option>
</select>
<div id="container" style="height:100%;"></div>
And file contents you have to load over AJAX. But in the case if you do not understand how to load it then ask me and I will write it.
The screenshot of this recommended solution
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 9 years ago.
Improve this question
I am working on a project where I need to dynamically build up web content coming from different sources (same domain). All the main libraries and css are managed centrally but depending on a configuration I have to add pages (html and embedded JavaScript) as 'tab content'.
My first approach was using iframes and it worked well besides the annoyance that I had some duplicte code (especially the header). I also had some issues with frame sizes but I could handle that in the end.
Now of course I read that iframes are evil but almost all the alternatives I found were using php or something else server-side. Unfortunately my web server does not provide anything like that, so I came across jQuery's load function. My first attempt is satisfying but what unsettles me now is managing (global) variables (and functions). Because jQuery.load() simply inserts the code into the DOM I always need to be extremely careful about naming here. For example I usually have a function called init() which is included in the body for the onload-Event. But every other page will need one of those as well.
The project is very likely to grow (maybe at some point even external developers will take part). I am now at a point where I have to decide which path to take and I am torn.
So my Question: Even though iframes are evil, im my experience it is a lot easier and secure than inserting the code via jQuery (in this particular case). Oh, that was not a question... The question would be: Is there any way to encapsulate variables and functions into the content that I load with jQuery? Or might there be a better way of handling variables/functions in this case anyways.
I am grateful for any suggestions.
I don't want to suggest to do it this way, but because you want to to know how this can be done, here some code to illustrate how it could be done (only rudimentary tested and should not be used without further testing).
While I'm not sure if you could (and i think you should not) structure your project that way i hope that it will help to solve your problem.
The main code of your page:
var app = {};
(function() {
var nextId = 1;
var elementById = {};
app.registerContent = function( id, initFunction ) {
try {
initFunction(elementById[id]);
} catch( e ) {
//if an error happend in the init script catch it so that the script will not break
console.error(e);
}
//remove the jquery element from the list
delete elementById[id];
}
//custom loader to handle init script
jQuery.fn.customLoader = function(url) {
var elm = $(this);
var id = nextId++;
//store the element where the data is loaded to in a list
elementById[id] = elm;
$.get(url,function(data) {
//replace the id in the script so that the element the data is loaded into
// can be passed to the init script later.
data = data.replace("%%unique-id%%",id);
elm.html(data); //append the data to the element
});
}
//a sample how you would load the content
$(function() {
$(".dest").customLoader("content.html");
});
})();
The code in your requested data:
<div>some content</div>
<!-- the script that correspons to that part -->
<script>
//create a scope using a function that is executed directly
(function() {
//using var to make the variables only visible to this place
var uniqueId = %%unique-id%%; //this will be replaced by the loader to identify the element where the data is loaded to
var someVar = 1;
app.registerContent(uniqueId, initFunctionForTheContent)
function initFunctionForTheContent(element) {
element.css("background-color", "red")
}
})();
</script>
Not having a back end with a huge app and many developers sounds like hell. Sometimes things are best fitted on the server side e.g php's include.
Either way I would go with an MVC solution.
I have experience with backbone and underscore so I will give an example with that but there are many other MVC solutions.
If all you need to do is load some data you can use underscores templating which is very lightweight
You can do stuff like:
In the model:
sum: function() {
Sum certain properties of your objects.
}
In index.html
<script type="text/template" id="sum-template">
sum: <% print(sum) %>
</script
In the view
sumTemplate: _.template($('#sum-template').html()),
this.$el.find('#sum').html(this.sumTemplate({sum:Expenses.sum()}));
As you can see you can get data from different places and load them to certain HTML elements. It easy to manage on the long run.
In order to persist data you can free and paid hosted data bases a simple google search for
redis hosting, mongodb hosting etc... so you are not dependent on your current server.
I'm slowly getting a better understanding of JavaScript but I'm stuck on how best to tackle this particular organization/execution scenario.
I come from a C# background and am used to working with namespaces so I've been reading up on how to achieve this with JavaScript. I've taken what was already starting to become a large JavaScript file and split it out into more logical parts.
I've decided on a single file per page for page specific JavaScript with anything common to two or more pages, like reusable utility functions, in another namespace and file.
This makes sense to me at the moment and seems to be a popular choice, at least during the development process. I'm going to use a bundling tool to combine these disparate files for deployment to production anyway so anything that makes development more logical and easier to find code the better.
As a result of my inexperience in dealing with lots of custom JavaScript I had a function defined in the common JavaScript file like this:
common.js
$(document).ready(function () {
var historyUrl = '/history/GetHistory/';
$.getJSON(historyUrl, null, function (data) {
$.each(data, function (index, d) {
$('#history-list').append('<li>' + d.Text + '</li>');
});
});
});
This is obviously far from ideal as it is specific to a single page in the application but was being executed on every page request which is utterly pointless and insanely inefficient if not outright stupid. So that led me to start reading up on namespaces first.
After a bit of a read I have now moved this to a page specific file and re-written it like this:
Moved from common.js to historyPage.js
(function(historyPage, $, undefined) {
historyPage.GetHistory = function () {
var historyUrl = '/history/GetHistory/';
$.getJSON(historyUrl, null, function (data) {
$.each(data, function (index, d) {
$('#history-list').append('<li>' + d.Text + '</li>');
});
});
};
}( window.historyPage = window.historyPage || {}, jQuery ));
I found this pattern on the jQuery Enterprise page. I'm not going to pretend to fully understand it yet but it seems to be a very popular and the most flexible way of organizing and executing JavaScript with various different scopes whist keeping things out of the global scope.
However what I'm now struggling with is how to properly make use of this pattern from an execution point of view. I'm also trying to keep any JavaScript out of my HTML Razor views and work in an unobtrusive way.
So how would I now call the historyPage.GetHistory function only when it should actually execute ie: only when a user navigates to the History page on the web site and the results of the function are required?
From looking at the code, it would seem that the easiest test would be to check if the page you are on contains an element with an id of history-list. Something like this:
var $histList = $('#history-list');
if($histList.length > 0){
// EXECUTE THE CODE
}
Though if it really only ever needs to run on one given page, maybe it's just not a good candidate for a shared javascript file.
Using the code I have detailed above in the question I have gotten it working by doing the following:
In _Layout.cshtml
#if (IsSectionDefined("History"))
{
<script type="text/javascript">
$(document).ready(function () {
#RenderSection("History", required: false)
});
</script>
}
In History.cshtml
#section History
{
historyPage.GetHistory();
}
The code is executing as required only when the user requests the History page on the web site. Although the comment from #Dagg Nabbit above has thrown me a curve ball in that I thought I was on the right track ... Hmm ...
I'm writing a small JavaScript framework for fun and possible implementation similar to backbone(hence the tag). I've started saving elements as object properties, as shown below. I'm not sure if I've seen this done, so I was curious if this causes any issues.
Similarly, If the module depends on other modules I list those at the top of the object in the form of....another object.
I wanted a way to list dependencies ( page elements or JavaScript modules ) and detect any issues up front. This has similar ( not same ) benefits as dependency injection.
This is a specific question on this code review post which explains a bit further on how the framework works.
/*MUserTry
**
**
**
*/
$A.modelAjax({
Name: 'MUserTry',
S: {
DynSma: SDynSma,
DynTwe: SDynTwe,
DynArc: SDynArc,
AniFlipPage: SAniFlipPage,
ClientStorage: SClientStorage
},
E: {
but: $A('#ut_but')[0]
},
J: {
box: $('#ut_box')
},
init: function () {
var pipe = {},
this_hold = this;
this.J.box.draggable();
this.E.but.addEventListener("click", function () {
pipe = $A.definePipe(this_hold.Name);
$A.ajaxMachine(pipe);
}, false);
},
pre: function (pipe) {
pipe.page.email = this.e_button.getAttribute('data-email');
pipe.proceed = true;
},
post: function (pipe) {
this.S.ClientStorage.setAll(pipe.server.smalls);
this.S.DynSma.run(pipe.server.smalls);
this.S.DynArc.run(pipe.server.arcmarks);
this.S.DynTwe.run(pipe.server.tweets);
this.S.AniFlipPage.run('ma');
},
finish: function (pipe) {
$A.log(pipe);
}
});
Ok first off let me offer the obligatory "you'll never get a better wheel by re-inventing the wheel" warning. Whatever you're trying to accomplish, you're almost certainly going to be more successful with it if you use an existing library. And even if there is good cause for you to make your own, it would still benefit you immensely to at least look at existing libraries.
But ... maybe you're just having fun with this project, and looking at other projects isn't fun so you're not doing it. Fair enough.
In any case, if you do look at Backbone, you'll find that this practice is core to the Backbone View class. Every View in Backbone has an "el" and "$el" property, which refer to the raw DOM element for the view and the jQuery-wrapped version of that element.
Backbone has no real performance issues with this because variables/properties in JS are just pointers; in other words, when you set the property of an object to an element, you aren't duplicating that element, you're just adding a reference to it (to put it another way, it's more like you're an A tag rather than a whole new document).
The one time Backbone does have a problem though (and your framework will too) is with stale references. In other words, if you just remove element X from the page, the browser will stop using memory for it (eventually, via garbage collection). But if there is an object out there which points to that element, it won't get garbage-collected, because anything with a reference isn't "garbage".
So, the main thing you have to watch out for is making sure that these objects either:
A) get deleted whenever their elements do, or
B) get rid of their references (eg. delete obj.reference) when their elements get deleted
If you don't do that, things will still probably work just fine ... until you use it on a page with lots of elements being created/deleted, at which point Firefox will start popping up "this script took way too long to run, are you really sure you want to be doing this?" messages.