I'm working through some javascript examples, and I just did this one:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Page title</title>
<script type="text/javascript">
function displayText()
{
document.getElementById('targetDIV').innerHTML = "You're using Javascript";
}
</script>
</head>
<body onload="displayText()">
<h2>This should be before the other text.</h2>
<div id="targetDIV">
</div>
</body>
</html>
OK. Very basic, I know-but I realized I was confused about the "why" of some things. Could it be accurate to say that:
Function=WHAT will happen.
The call (the body onload...)= WHEN it will happen.
and div id="targetDIV" = WHERE it will happen
I know this is the case in this example, but in general is that the way things work in Javascript?
Yes, that's a pretty good working model to carry in your head.
onload for the body is called an Event and many objects issue events. Your function displayText is called in response to the onload Event and is therefore an event handler.
The code inside your function can do anything, but in this case it dynamically loads some text into a tag on your page.
There are a couple of other things worth pointing out at this point. You access the tag using document.getElementById. document is variable available to you in Javascript which contains a model of the page called the DOM or document object model. This is extremely powerful as it presents a hierarchical layout of everything on your page and allows you to manipulate the contents.
getElementById() is a very useful function which searches the DOM tree and returns the object which has the ID that you specify, it's a sort of search. The text gets to your tag because you added the targetDIV id to the DIV tag and therefore you could find it via the DOM function.
Welcome to programming in Javascript. Now you have a goood working model you'll find loads of really clever things you can do and your life as a web programmer will never be the same again.
Sound good to me.
Related
This is not a duplicate question for these reasons:
I am asking about how to replace the entire HTML document with JavaScript without jQuery or any other fancy extensions to JavaScript. Some of the other questions that are similar to this question deal with specific things like AJAX or jQuery.
I am NOT asking about why document.write() only appends to the page. Perhaps the pure JavaScript solution I am looking for may incorporate that function, but it cannot only be that since it is inadequate by itself.
What I am looking to do is overwrite a webpage as it is displayed in the browser with only HTML. The function document.write() only appends whatever argument is passed to it to the document's body. The property document.documentElement.outerHTML can be read from, but unlike when it is used on a page's child elements, cannot be written to, and even if it could, it would leave the DOCTYPE untouched.
I am working on a bookmarklet, so this JavaScript would not run in the page, meaning there is no problem with the script being overwritten while it is running. It could also be run in the browser's developer tools.
As an example, suppose I have about:blank opened in my browser. The contents of the DOM would look like this:
<html>
<head></head>
<body></body>
</html>
I want to be able to overwrite it with whatever string I want. So, for instance, I could make it look like this:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Example</title>
</head>
<body>
<p>This is an example.</p>
</body>
</html>
How can I achieve that sort of overwrite of a document?
Try this:
function one() {
document.write('<html><body><pre>the first html</pre></body></html>');
document.close(); // this makes the difference
}
function two() {
document.write('<html><body><pre>the second html</pre></body></html>');
document.close();
}
Refer to linstantnoodles' answer in question document.write() overwriting the document?, the document.open is implicitly called before the document.write is called, but the document.close doesn't, and
document.write() when document is closed = rewrite the document;
document.write() when document is open = append to the document.
You can use document.implementation.createDocumentType to rewrite the doctype and document.getElementsByTagName to get the DOM elements, then rewrite with innerHTML and setAttribute.
var newDoctype = document.implementation.createDocumentType('html','-//W3C//DTD XHTML 1.0 Transitional//EN','http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtdd');
document.doctype.parentNode.replaceChild(newDoctype,document.doctype);
document.getElementsByTagName('html')[0].setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
var doc = document.getElementsByTagName('html')[0];
doc.getElementsByTagName('head')[0].innerHTML = '<title>Example</title>';
doc.getElementsByTagName('body')[0].innerHTML = '<p>This is an example.</p>';
<!DOCTYPE html>
<html>
<head>
</head>
<body>
</body>
</html>
Edit:
Updated to include comments by Xweque and xmlns attribute.
document.write('Some Text')
document.write rewrites the page's code.
I have a simple html page is as-
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Untitled Document</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script>
$(document).ready(function(e) {
var s = $("#d").get(0).getElementsByTagName("a");
for(var x=0;x<s.length;x++){
document.writeln(s.length);
}
var k=document.createElement("a");
k.innerHTML="hello";
var q=document.getElementById("d");
q.appendChild(k);
});
</script>
</head>
<body>
<div id="d">
<a><img class="zzz"/></a>
<a><img class="zzz"/></a>
</div>
</body>
</html>
Javascript executes only up to for loop (element k is not appended). If for loop is removed then only element k got appended. Isn't code below for loop is supposed to be executed after for loop execution?
Welcome to StackOverflow! A few suggestions to help you as you are learning to develop:
Always use meaningful variable names, it's hard to keep track of what
single letter variables represent
Don't use document.write for
debugging, use the console for that
If you are using a library like
jQuery, try to use it as much as you can when you are beginning to
learn it. Only fall back to basic javascript if you have a compelling
reason to do so
Your function can be rewritten in pure jQuery like so:
$(function() {
var container = $('#d');
console.log(container.find('a').length);
container.append('<a>hello</a>');
});
I haven't run your code, but your error probably has to do with this statement: $("#d").get(0).getElementsByTagName("a");. The eq() method returns a jQuery object, which does not have a getElementsByTagName() method on it.
If you want a plain DOM object without the jQuery wrapper you can address the jQuery object like an array: $('#d')[0].getElementsByTagName("a");, though like I said, it's best to stick with the jQuery library if you are going to use it.
Good luck as you learn!
you can't use writeln (or write) after the document has been loaded(or I better say: you shouldn't, because it will overwrite the complete document, including #d , what will force the error, because this element isn't available anymore after the usage of writeln )
I guess you want something like this:
$(document).ready(function(e) {
$('a').prepend(function(i){return i;});
$('#d').append('hello');
});
Demo: http://jsfiddle.net/stRBU/2/
I am very confused.
I created the following script which is located at http://tapmeister.com/test/dom.html. For some unknown reason, tinymce.editors.ta1 and tinymce.editors[0] show up as undefined, and attempting to use a method under them results in an error. But when I inspect tinymce or tinymce.editors using FireBug, I see them in the DOM.
So, I create a jsfiddle http://jsfiddle.net/JWyWM/ to show the people on stackoverflow. But when I test it out, tinymce.editors.ta1 and tinymce.editors[0] are no longer undefined, and the methods work without error.
What is going on??? Maybe something to do with public/protected/private properties? How do I access methods such as tinymce.editors.ta1.hide()? Thank you!!!
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Testing</title>
<script src="http://tinymce.cachefly.net/4.0/tinymce.min.js"></script>
<script type="text/javascript">
tinymce.init({selector: "textarea#ta1"});
tinymce.init({selector: "textarea#ta2"});
console.log(tinymce);
console.log(tinymce.editors);
console.log(tinymce.editors.ta1);
console.log(tinymce.editors[0]);
//tinymce.editors.ta1.hide();
//alert('pause');
//tinymce.editors.ta1.show();
</script>
</head>
<body>
<form>
<textarea id="ta1"></textarea>
<textarea id="ta2"></textarea>
</form>
</body>
</html>
TinyMCE doesn't do all of the setup work immediately when you call init. It provides a callback, setup, to tell you when the work is done.
So if you provide a setup callback, you can interact with the editor instance then.
Here's an example (I've also moved your scripts to the end, which is best practice regardless):
Live Example | Live Source
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=ISO-8859-1" />
<title>Testing</title>
</head>
<body>
<form>
<textarea id="ta1"></textarea>
<textarea id="ta2"></textarea>
</form>
<script src="http://tinymce.cachefly.net/4.0/tinymce.min.js"></script>
<script type="text/javascript">
tinymce.init({
selector: "#ta1, #ta2",
setup: function(e) {
console.log("Editor " + e.id + " is ready");
}
});
</script>
</body>
</html>
Now, if you want to actually access the editor instance, bizarrely TinyMCE doesn't add it to tinymce.editors until after calling the setup function. But if you throw in a brief yield, you're all set. Here's the above with a changed setup function:
Live Copy | Live Source
setup: function(e) {
// Bizarrely, TinyMCE calls `setup` *before* adding
// the relevant editor to `tinymce.editors`,
// so we have to yield briefly
console.log("Editor " + e.id + " is ready");
if (e.id === "ta2") {
console.log("It's ta2, I'll hide it in a moment.");
setTimeout(function() {
tinymce.editors[e.id].hide();
}, 0);
}
}
So why did it work on jsFiddle? Well, jsFiddle has a truly brain dead surprising default setting, which is to put all of your script in a window#load callback function. window#load happens very late in the load process, after all external resources have been loaded. (You can see that in the jsFiddle UI, it's the second drop-down list on the left.) So apparently TinyMCE was completely ready at that point, where it isn't earlier in the cycle.
Side note: 99.9% of the time, there is absolutely no point in supplying a tag name with an id selector, e.g. textarea#ta1. id values are unique, so you don't have to qualify them unless you explicitly want to avoid matching an element that may sometimes have one tag name, or other times have another, which is a pretty unusual use case.
There's a large chance that your script is running before tinyMCE has actually loaded. It might be the case that it loads faster from your test site so that is why it works.
Use as a quick check.
I have found several other questions here on S.O. (and the web in general) that ask roughly this same question, but the answers always seem to suggest other ways of structuring code that avoid the need for addressing this underlying issue.
For example, many people suggest (and a good suggestion, I agree) to put your code in the jquery load method's callback, on the calling page and not the called page. However I have unique scripts that may appear in certain resources, so I would not want to do that for every load and nor do I necessarily know what these scripts will be.
Here is a test setup to demonstrate what I'm trying to do. The short summary is that when I load partial.htm from main.htm, its script does not fire.
main.htm:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>main file</title>
</head>
<body>
<ul id="links">
<li>some page1</li>
<li>some page 2</li>
<li>some other partial page</li>
</ul>
<div id="panel" style="display:none; padding:20px; background-color:#CCC;">
LOADED CONTENT WILL GO HERE
</div>
<script type="text/javascript" src="/path/to/jquery-1.3.2.min.js"> </script>
<script type="text/javascript">
$(function(){
$('#links a').click(function() {
var $panel = $('#panel');
$panel.show();
$panel.html('Please wait...');
var href = $(this).attr('href');
$('#panel').load(href + ' #content');
return false;
});
});
</script>
</body>
</html>
OK, very simple functionality on this page. Imagine there are many more links, and some of them may require scripting while others do not.
Here is partial.htm:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>partial file</title>
</head>
<body>
<div id="content">
<p>Hey, I am the partial file!</p>
<script type="text/javascript">
alert('I am some JS in the partial file! But sadly I do not execute...');
</script>
</div>
<div>
I am some other content on the page that won't be included by jquery.load()...
</div>
</body>
</html>
Notice that my script in partial.htm does not fire. So, my question remains: how to get this to fire, excluding any answers that tell me to put this in the .load() method's callback. (This would be because I may not have the fore-knowledge of which scripts these partial pages may contain or require!)
Thank you!
Update #1:
I suppose an acceptable answer is simply "you can't." However, I'd like to know if this is definitively the case. I haven't been able to find anything that officially states this yet.
Also, when I use firebug to inspect the panel region afterwards, there is no script element present at all. It is as if it is being parsed out by load.
Update #2:
I've narrowed this down to be a problem only when using the selector as part of the href. Loading the entire "somepage.html" will execute the script, but loading "somepage.html #someregion" does not.
$panel.load('somepage.html'); // my script fires!
$panel.load('somepage.html #someregion'); // script does not fire
I'm going to try and hunt down why this may be the case in the jquery source...
Well it seems that this is by design. Apparently to make IE happy, the rest of us suffer. Here's the relevant code in the jquery source:
// See if a selector was specified
self.html( selector ?
// Create a dummy div to hold the results
jQuery("<div/>")
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
.append(res.responseText.replace(/<script(.|\s)*?\/script>/g, ""))
// Locate the specified elements
.find(selector) :
// If not, just inject the full result
res.responseText );
I'm wondering if, instead of just stripping out the scripts, I could modify the jquery source to include them in some other way that makes IE happy? I still have yet to find anything else on the web discussing this matter, I'm sure I'm not the only person stumped by this?
I have run across issues before with IE not running injected <script>s that didn't contain the defer attribute. This discussion thread has some good information about the topic: innerHTML and SCRIPT tag
I read that you should define your JavaScript functions in the <head> tag, but how does the location of the <script> (whether in the <head>, <body>, or any other tag) affect a JavaScript function.
Specifically, how does it affect the scope of the function and where you can call it from?
Telling people to add <SCRIPT> only in the head sounds like a reasonable thing to do, but as others have said there are many reasons why this isn't recommended or even practical - mainly speed and the way that HTML pages are generated dynamically.
This is what the HTML 4 spec says :
The SCRIPT element places a script
within a document. This element may
appear any number of times in the HEAD
or BODY of an HTML document.
And some sample HTML. Doesn't it look pretty all formatted here :)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<HTML>
<HEAD>
<TITLE>A document with SCRIPT</TITLE>
<META http-equiv="Content-Script-Type" content="text/tcl">
<SCRIPT type="text/vbscript" src="http://someplace.com/progs/vbcalc">
</SCRIPT>
</HEAD>
<BODY>
<SCRIPT type="text/javascript">
...some JavaScript...
</SCRIPT>
</BODY>
</HTML>
And something to look forward to in HTML 5 :
New async attribute in <SCRIPT> :
Note: There are ways [sic] a script can be
executed:
The async attribute is "true": The
script will be executed asynchrously
with the rest of the page, so the
script will be executed while the page
continues the parsing.
The async attribute is "false", but
the defer attribute is "true": The
script will be executed when the page
is finished with the parsing.
The normal rules of play still stand; don't use stuff before it's defined. :)
Also, take note that the 'put everything at the bottom' advice isn't the only rule in the book - in some cases it may not be feasible and in other cases it may make more sense to put the script elsewhere.
The main reason for putting a script at the bottom of a document is for performance, scripts, unlike other HTTP requests, do not load in parallel, meaning they'll slow down the loading of the rest of your page. Another reason for putting scripts at the bottom is so you don't have to use any 'DOM ready' functions. Since the script tag is below all elements the DOM will be ready for manipulation!
EDIT: Read this: http://developer.yahoo.com/performance/rules.html#js_bottom
One of the aspects of placement is performance. See this fine article within the YSlow discussion for why it's sometimes recommended you put them at the bottom of the document.
As for issues of scope, the usual visibility rules for Javascript (vars defined inside or outside of functions, local, global, closures, etc.) are not affected so far as I know.
Position of script tag does matter.
If you bind a Function with document Element then the document element has to be loaded first before we implement function. suppose getTeachers() is function in getTeachers.js file.
This will give you an error:
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Unit Teachers</title>
<head>
<script type="text/javascript" src="getTeachers.js"></script>
<script type="text/javascript">
document.getElementById("buttonId").onclick=function(){getResults()};
</script>
</head>
<body>
<form>
<input type = "button" id="buttonId" value = "Press for Results" /><br />
</form>
<span id="results" /></span>
</body>
</html>
It gives error before head is loaded first and it cannot find element with id specified.
The below code is correction:
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
<title>Unit Teachers</title>
<head>
<script type="text/javascript" src="getTeachers.js"></script>
</head>
<body>
<form>
<input type = "button" id="buttonId" value = "Press for Results" /><br />
</form>
<script type="text/javascript">
document.getElementById("buttonId").onclick=function(){getResults()};
</script>
<span id="results" /></span>
</body>
</html>
If your script refers to an ID on the page and the page has not been rendered (i.e. script is before HTML, or your script is executed with onload, rather then the DOM is ready) you can also get an error.
It doesn't. Most programming framework scatter scripts all throughout the page. I've only rarely seen problems because of that (and only from older browsers).
If you pull Javascripts in through XMLHttpRequest, like Diodeus said, it probably won't work. In my case, there was no error, the browser just ignores the new script(s).
I ended up using this, not terribly elegant but works for me so far:
http://zeta-puppis.com/2006/03/07/javascript-script-execution-in-innerhtml-the-revenge/
How to use execJS: http://zeta-puppis.com/2006/02/23/javascript-script-execution-in-innerhtml/
Note: Watch out for < in this line: for(var i=0;i<st.length; i++)
If you have an inline script (outside functions) located before functions it may call, you may get an error because they may not be not available yet. Not saying it is always going to happen, just that it may depending on browser type or version.
Javascript's scoping rules are similar to perl - you can call any function at the current or any higher scope level. The only restriction is that the function has to be defined at the time you call it. The position in the source is irrelevant - only the position in time matters.
You should avoid putting scripts in the <head> if possible as it slows down page display (see the link Alan posted).