I have a website with the typical footer "About Me", "About this site", ... and when the user clicks any of those, a modal/popup is displayed and its innerHTML set via JavaScript
var aboutMe = "I am Foo and I like ..."; // 500 characters;
var aboutTheSite = "This site is for ..."; // 500 characters;
var cookiePolicy = "This site uses cookies ..."; // 500 characters;
// ...
// ...
The alternative for doing this is to create some tags and append that text
<div id='aboutMeContent'>I am Foo and I like ... </div>
<!-- ... .... more -->
I didn't do the second option because I like to keep my source code really clean (YouTubeGo.net). But I am a bit worried about this worsening the performance of the site. All the JavaScript is minimized in a 30K file, but without aboutMe, aboutThisSite... it would be 20K, though those 10K would be loaded inside the .html.
The question:
Which of both options is better?
Or would it still preferable to load it using AJAX and forget about what I am doing?
And a little more technical question (I'm curious):
Does a browser load faster a JavaScript file with only var foo = "fooBar"; and variable declarations than one with really dense code? I know from experience that it doesn't read the contents of an object or a function until it is called or requested.
I care very much about performance time but I can't tell you what was I thinking when I implemented the first option.
Related
my project is the survey with several pages. I want to upload answers of several pages into the same uniqueID. Now I can get uniqueID from a page js file, but how can I transport this uniqueID to other js files so I can upload the data to the same uniqueID.
The problem can be reformulated to how have a variable value accessible through out varying pages and js files.
My prefered way to do it - with lots of data - is a base page invisible to the user loading a "global script". This base page loads and unloads all other pages (including js and css files). Since the variable in your case the firebaseID is globally defined it works.
If its not sensible data you can also put it to the LocalStorage (preferable encrypted) This would also help if a user wants to pause the survey and later on to finish it. (You have 5MB on mobile devices and 10 MB on desktop).
More details on solution one:
The easy part is to insert HTML into other HTML. I use server generated views so the base page gets HTML added and removed as needed.
The harder part is the page (view) specific loading of js.In my use case the users can jump around via a navigation until they finally submit and finish. If you only allow sequential moving forward and no backward its easier. I actually mimic a restful behavior. Some building blocks in the base script include
// Some global vars
var statMsg = "";
var navTargetInitial= "app";
var navTarget;
var htmlDir = "app"; // directory of the html templates
var scriptDir = "js"; // directory of the user scripts
var routes = [
{
navTarget: "main",
loadScript: true,
navTitle: "Survey page 1",
navMenutext: "Page 1"
}, {
navTarget: "app",
loadScript: true,
navTitle: "Survey page 2",
navMenutext: "Page 2"
},
....
];
// Then docReady beside other stuff
function docReady() {
// other stuff
nav2Page(navTarget);
}
// here we handover data, put relevant things to sessionStorage
window.onbeforeunload = function (e) {
// Do stuff
};
// here we cleanup
window.onunload = function () {
// cleanup
};
The crucial part is loading the new js after the html is in DOM so an essential part is an eventhandler on the event DOM loaded
document.addEventListener("DOMContentLoaded",
loadJScript(navScript, function(response, status, xhr ){// error handling }
The rest is ajax, error handling, reaction to clicks on going back/ forward in the page history and reacting accordingly to it.
Hope this helps
I am learning javascript and to tell the truth, some parts don't make sense to me. like this one. I wrote this block of code first :
<body>
<script type="text/javascript">
function people(name, age){
this.name = name;
this.age = age;
this.ret = yearsLeft;
}
function yearsLeft(){
var numYears = 65 - this.age;
return numYears;
}
var sam = new people("sam forest", 39);
var billy = new people("billy wood", 45);
document.write(billy.ret());
</script>
</body>
and I got the result. However I wrote this one after the first one and I got the same result:
<head>
<title>Javascript</title>
<script type="text/javascript">
function people(name, age){
this.name = name;
this.age = age;
this.ret = yearsLeft;
}
function yearsLeft(){
var numYears = 65 - this.age;
return numYears;
}
var sam = new people("sam forest", 39);
var billy = new people("billy wood", 45);
</script>
</head>
<body>
<script type="text/javascript">
document.write(billy.ret());
</script>
</body>
Here is my question, what is the difference , when I get the same result in both ways?
From Yahoo's Best Practices for Speeding Up Your Web Site:
The problem caused by scripts is that they block parallel downloads.
The HTTP/1.1 specification suggests that browsers download no more
than two components in parallel per hostname. If you serve your images
from multiple hostnames, you can get more than two downloads to occur
in parallel. While a script is downloading, however, the browser won't
start any other downloads, even on different hostnames.
In some situations it's not easy to move scripts to the bottom. If,
for example, the script uses document.write to insert part of the
page's content, it can't be moved lower in the page. There might also
be scoping issues. In many cases, there are ways to workaround these
situations.
An alternative suggestion that often comes up is to use deferred
scripts. The DEFER attribute indicates that the script does not
contain document.write, and is a clue to browsers that they can
continue rendering. Unfortunately, Firefox doesn't support the DEFER
attribute. In Internet Explorer, the script may be deferred, but not
as much as desired. If a script can be deferred, it can also be moved
to the bottom of the page. That will make your web pages load faster.
Therefore, in general, it is preferrable to put them at the bottom. However, it isn't always possible, and it often doesn't make that much of a difference anyway.
In many cases the result is the same, but there's a relevant difference due to the way web browser render html pages.
Since a browser reads the page content top-to-bottom, placing the javascript code within the <head> tag will cause the actual page content to be displayed after the browser has finished parsing the script. Placing it just before the </body> tag instead will let the browser display the content faster, which is usually desirable.
Another implication of the top-to-bottom rendering is related to SEO optimization: since most crawlers will inspect a fixed number of bytes at the top of a page, having those first bytes filled with javascript code will prevent the crawler from accessing the actual page content, therefore reducing the benefits of whatever you've SEO-wise.
Because you're doing pretty much the same thing. The browser is going to evaluate your javascript code sequentially, and, if it's just statements, as you putted, they're going to be executed.
So, one thing you need to pay attention is that the browser is going to evaluate your hole document (html, css, javascript), and the javascript statements that are not function definitions are going to be executed right away.
I'm creating a simple "try it yourself" code editor like the one found on W3schools. Looking at the source, it seems all that one does is use JavaScript's document.write() command to write whatever is entered into the textarea on the left into the iframe on the right, without any sanitation:
function submitTryit() {
var text = document.getElementById("textareaCode").value;
var ifr = document.createElement("iframe");
ifr.setAttribute("frameborder", "0");
ifr.setAttribute("id", "iframeResult");
document.getElementById("iframewrapper").innerHTML = "";
document.getElementById("iframewrapper").appendChild(ifr);
var ifrw = (ifr.contentWindow) ? ifr.contentWindow : (ifr.contentDocument.document) ? ifr.contentDocument.document : ifr.contentDocument;
ifrw.document.open();
ifrw.document.write(text);
ifrw.document.close();
}
Is that secure in itself, or am I missing something inside the w3school's editor that does something more for security's sake?
Thanks,
If that javascript is not persisted in such a way that it can render to browsers of users other than its authors, you should be fine. Otherwise you are creating a platform that can be used to propagate XSS worms. Just allowing a user to execute arbitrary javascript on their own page is no less secure than that most modern browsers having a debugging console that lets a user execute javascript.
Is there a way for a function (called by an IPython Notebook cell) to retrieve the content of a JavaScript variable (for example IPython.notebook.notebook_path which contains the path of the current notebook)?
The following works well when written directly within a cell (for example, based on this question and its comments):
from IPython.display import display,Javascript
Javascript('IPython.notebook.kernel.execute("mypath = " + "\'"+IPython.notebook.notebook_path+"\'");')
But that falls apart if I try to put it in a function:
# this doesn't work
from IPython.display import display,Javascript
def getname():
my_js = """
IPython.notebook.kernel.execute("mypath = " + "\'"+IPython.notebook.notebook_path+"\'");
"""
Javascript(my_js)
return mypath
(And yes, I've tried to make global the mypath variable, both from within the my_js script and from within the function. Also note: don't be fooled by possible leftover values in variables from previous commands; to make sure, use mypath = None; del mypath to reset the variable before calling the function, or restart the kernel.)
Another way to formulate the question is: "what's the scope (time and place) of a variable set by IPython.notebook.kernel.execute()"?
I think it isn't an innocuous question, and is probably related to the mechanism that IPython uses to control its kernels and their variables and that I don't know much about. The following experiment illustrate some aspect of that mechanism. The following works when done in two separate cells, but doesn't work if the two cells are merged:
Cell [1]:
my_out = None
del my_out
my_js = """
IPython.notebook.kernel.execute("my_out = 'hello world'");
"""
Javascript(my_js)
Cell [2]:
print(my_out)
This works and produces the expected hello world. But if you merge the two cells, it doesn't work (NameError: name 'my_out' is not defined).
I think the problem is related with Javascript being asynchronus while python is not. Normally you would think that the Javascript(""" python cmd """) command is executed, and then your print statment should work properly as expected. However, the Javascript command is fired but not executed. Most pobably it is executed after the cell 1 execution is fully completed.
I tried your example with sleep function. Did not help.
The asnyc problem can esaily be seen by adding an alert statement within my_js, but before kernel.execute line. The alert should be fired even before trying a python command execution.
But at the presence of print (my_out) statement within cell 1, you will again get the same error without any alerts. If you take the print line out, you will see the alert poping out within cell 1. But the varibale my_out is set afterwards.
my_out = None
del my_out
my_js = """
**alert ("about to execute python comand");**
IPython.notebook.kernel.execute("my_out = 'hello world'");
"""
Javascript(my_js)
There are other javascript utilities within notebook like IPython.display.display_xxx which varies from displaying video to text object, but even the text object option does not work.
Funny enough, I tested this with my webgl canvas application which displays objects on the HTML5 canvas; display.display_javascript(javascript object) works fine ( which is a looong html5 document) while the two pieces of words of output does not show up?! Maybe I should embed the output into canvas application somewhere, so it s displayed on the canvas :)
I wrote a related question (Cannot get Jupyter notebook to access javascript variables) and came up with a hack that does the job. It uses the fact that the input(prompt) command in Python does block the execution loop and waits for user input. So I looked how this is processed on the Javascript side and inserted interception code there.
The interception code is:
import json
from IPython.display import display, Javascript
display(Javascript("""
const CodeCell = window.IPython.CodeCell;
CodeCell.prototype.native_handle_input_request = CodeCell.prototype.native_handle_input_request || CodeCell.prototype._handle_input_request
CodeCell.prototype._handle_input_request = function(msg) {
try {
// only apply the hack if the command is valid JSON
console.log(msg.content.prompt)
const command = JSON.parse(msg.content.prompt);
const kernel = IPython.notebook.kernel;
// return some value in the Javascript domain, depending on the 'command'.
// for now: specify a 5 second delay and return 'RESPONSE'
kernel.send_input_reply(eval(command["eval"]))
} catch(err) {
console.log('Not a command',msg,err);
this.native_handle_input_request(msg);
}
}
"""))
The interception code looks whether the input prompt is valid JSON, and in that case it executes an action depending on the command argument. In this case, it runs the commend["eval"] javascript expression and returns the result.
After running this cell, you can use:
notebook_path = input(json.dumps({"eval":"IPython.notebook.notebook_path"}))
Quite a hack, I must admit.
Okay, I found a way around the problem: call a Python function from Javascript and have it do all of what I need, rather than returning the name to "above" and work with that name there.
For context: my colleagues and I have many experimental notebooks; we experiment for a while and try various things (in a machine learning context). At the end of each variation/run, I want to save the notebook, copy it under a name that reflects the time, upload it to S3, strip it from its output and push it to git, log the filename, comments, and result scores into a DB, etc. In short, I want to automatically keep track of all of our experiments.
This is what I have so far. At the bottom of my notebooks, I put:
In [127]: import mymodule.utils.lognote as lognote
lognote.snap()
In [128]: # not to be run in the same shot as above
lognote.last
Out[128]: {'file': '/data/notebook-snapshots/2015/06/18/20150618-004408-save-note-exp.ipynb',
'time': datetime.datetime(2015, 6, 18, 0, 44, 8, 419907)}
And in a separate file, e.g. mymodule/utils/lognote.py:
# (...)
from datetime import datetime
from subprocess import call
from os.path import basename, join
from IPython.display import display, Javascript
# TODO: find out where the topdir really is instead of hardcoding it
_notebook_dir = '/data/notebook'
_snapshot_dir = '/data/notebook-snapshots'
def jss():
return """
IPython.notebook.save_notebook();
IPython.notebook.kernel.execute("import mymodule.utils.lognote as lognote");
IPython.notebook.kernel.execute("lognote._snap('" + IPython.notebook.notebook_path + "')");
"""
def js():
return Javascript(jss())
def _snap(x):
global last
snaptime = datetime.now()
src = join(_notebook_dir, x)
dstdir = join(_snapshot_dir, '{}'.format(snaptime.strftime("%Y/%m/%d")))
dstfile = join(dstdir, '{}-{}'.format(snaptime.strftime("%Y%m%d-%H%M%S"), basename(x)))
call(["mkdir", "-p", dstdir])
call(["cp", src, dstfile])
last = {
'time': snaptime,
'file': dstfile
}
def snap():
display(js())
To add to the other great answers, there is a nuance of the browsers attempting to run the jupyter nb javascript magic on nb load.
To demonstrate: create and run the following cell:
%%javascript
IPython.notebook.kernel.execute('1')
Now save the notebook, close it and then re-open it. When you do that, under that cell suddenly you will see an error in red:
Javascript error adding output!
TypeError: Cannot read property 'execute' of null
See your browser Javascript console for more details.
That means the browser has parsed some js code and it tried to run it. This is the error in chrome, it will probably different in a different browser.
I have no idea why this jupyter javascript magic cell is being run on load and why jupyter notebook is not properly escaping things, but the browser sees some js code and so it runs it and it fails, because the notebook kernel doesn't yet exist!
So you must add a check that the object exists:
%%javascript
if (IPython.notebook.kernel) {
IPython.notebook.kernel.execute('1')
}
and now there is no problem on load.
In my case, I needed to save the notebook and run an external script on it, so I ended up using this code:
from IPython.display import display, Javascript
def nb_auto_export():
display(Javascript("if (IPython.notebook) { IPython.notebook.save_notebook() }; if (IPython.notebook.kernel) { IPython.notebook.kernel.execute('!./notebook2script.py ' + IPython.notebook.notebook_name )}"))
and in the last cell of the notebook:
nb_auto_export()
I am trying to start 3 applications from a browser by use of custom protocol names associated with these applications. This might look familiar to other threads started on stackoverflow, I believe that they do not help in resolving this issue so please dont close this thread just yet, it needs a different approach than those suggested in other threads.
example:
ts3server://a.b.c?property1=value1&property2=value2
...
...
to start these applications I would do
location.href = ts3server://a.b.c?property1=value1&property2=value2
location.href = ...
location.href = ...
which would work in FF but not in Chrome
I figured that it might by optimizing the number of writes when there will be effectively only the last change present.
So i did this:
function a ()
{
var apps = ['ts3server://...', 'anotherapp://...', '...'];
b(apps);
}
function b (apps)
{
if (apps.length == 0) return;
location.href = apps[0]; alert(apps[0]);
setTimeout(function (rest) {return function () {b(rest);};} (apps.slice(1)), 1);
}
But it didn't solve my problem (actually only the first location.href assignment is taken into account and even though the other calls happen long enough after the first one (thanks to changing the timeout delay to lets say 10000) the applications do not get started (the alerts are displayed).
If I try accessing each of the URIs separately the apps get started (first I call location.href = uri1 by clicking on one button, then I call location.href = uri2 by clicking again on another button).
Replacing:
location.href = ...
with:
var form = document.createElement('form');
form.action = ...
document.body.appendChild(form);
form.submit();
does not help either, nor does:
var frame = document.createElement('iframe');
frame.src = ...
document.body.appendChild(frame);
Is it possible to do what I am trying to do? How would it be done?
EDIT:
a reworded summary
i want to start MULTIPLE applications after one click on a link or a button like element. I want to achieve that with starting applications associated to custom protocols ... i would hold a list of links (in each link there is one protocol used) and i would try to do "location.src = link" for all items of the list. Which when used with 'for' does optimize to assigning only once (the last value) so i make the function something like recursive function with delay (which eliminates the optimization and really forces 3 distinct calls of location.src = list[head] when the list gets sliced before each call so that all the links are taken into account and they are assigned to the location.src. This all works just fine in Mozilla Firefox, but in google, after the first assignment the rest of the assignments lose effect (they are probably performed but dont trigger the associated application launch))
Are you having trouble looping through the elements? if so try the for..in statement here
Or are you having trouble navigating? if so try window.location.assign(new_location);
[edit]
You can also use window.location = "...";
[edit]
Ok so I did some work, and here is what I got. in the example I open a random ace of spades link. which is a custom protocol. click here and then click on the "click me". The comments show where the JSFiddle debugger found errors.