innerHTML side effects? - javascript

I'm having some issues with a DOM element reference and I think I've tracked it down to having something to do with updating innerHTML.
In this example, at the first alert, the two variables refer to the same element, as expected. What's strange though is that after updating the innerHTML of the parent element (body), the two variables are supposedly different, despite not touching either.
<html>
<head>
<script type="text/javascript">
var load = function () {
var div1 = document.createElement('div');
div1.innerHTML = 'div1';
div1.id = 'div1';
document.body.appendChild(div1);
alert(div1 === document.getElementById('div1')); // true
document.body.innerHTML += '<div>div2</div>';
alert(div1 === document.getElementById('div1')); // false
};
</script>
</head>
<body onload="load();">
</body>
</html>
Using == instead of === produces the same results. I get the same results in Firefox 3.5 and IE6. Any idea what's causing the second alert to evaluate to false?

WHen you get the innerHTML value of the body, add a string to it and put it back in the body, all elements in the body is recreated from the HTML string. What you have in the variable is a reference to an element that no longer exists in the page.

This is because ...
document.body.innerHTML += '<div>div2</div>';
... is not a true "append" .. it's a full replacement. Granted, the replacement is equal to the old content + the new content, the fact is that it is a new string which new DOM elements are built around.

Related

Getting value from a Input Box , Javascript, getElementById

SO i am trying to get the value or the contents of an HTML text box. I have tried various types of methods but none of them work. According to the debugger , the code stops at getelementbyid method. The commented lines are the methods that I have already tried. Some of them return null while some of them return NaN and most of them just return a blank page.
help is much appreciated.
<html>
<head>
<script type="text/javascript" >
function calculateit(){
document.open();
var number = document.getElementsByName('xyz')[0].value
//var number = document.getElementsByName("xyz").value;
//var number = document.getElementsByName('xyz').value;
//var number = document.getElementsByName("xyz");
//var number = document.getElementsByName('xyz');
//var number = document.getElementsById("xyz").value;
//var number = document.getElementsById('xyz').value;
//var number = document.getElementsById("xyz");
//var number = document.getElementsById('xyz');
//var number = document.form1.xyz.value; //form 1 was my form name and/or id
document.writeln(number);
var newtemp = 0;
var newtemp = tempera *9/5+32;
document.write(newtemp);
}
</script>
</head>
<body>
<input type="text" id="xyz" name="xyz">
<button title="calculate" onclick="calculateit()">calculate </button>
</body>
</html>
You're using the wrong method. The two widely supported methods for javascript are "getElementsByTagName" and "getElementById". Note exactly how "getElementById" is spelled. It is meant to get one element with the exact id that you specify. "getElementsByTagName" gets all elements of a certain tag...such as "div". When using "getElementById", you don't to index it or anything - it either returns null (can't find) or the exact element reference. From there, since it is a textarea, you can use ".value" to get the current value, like you already are.
ALSO:
You probably shouldn't use "document.write" or any of its related write methods AFTER the page has been rendered. In your example, that's exactly what you're doing, so once you get the ".value" stuff working, I would change that. The point is that "document.write" is more for during page rendering...so if you had javascript inline with the HTML body or something. Something like:
<html>
<head>
</head>
<body>
<script type="text/javascript">
document.write("testing");
</script>
</body>
</html>
would be fine, but still not preferred. The fact that you have it in a function, that is called on a button click, means it is not during page render, and shouldn't be used. A more practical approach is to have a <div> on the page and add text to it when necessary, using something like ".innerHTML". That way, things are dynamic and not overwritten in the actual document.
It's getElementById, not getElementsById.

Check if element legally supports the src attribute or innerHTML

Is there a way to check whether an element can display innerHTML or $.html() (like elems that have a separate closing tag) OR is an element whose is meant to have a src attribute according to the HTML spec such as <img>? I'm looking for fast/reliable way to do this via jQuery or native JavaScript.
Edit: According to the HTML spec, elements not designed to have inner content are called void elements but there are also elements like this <iframe src=url>inner</iframe> that are totally valid.
Unfortunately, there isn't a foolproof way to do this because in Javascript, any element can have those attributes.
Also, as odd at it may seem, almost all HTML elements, including <img>, have an innerHTML attribute, even though it can't really use it!
Your best bet is to make a table that specifies what elements have what.
<!DOCTYPE html>
<html>
<head>
<title>Test</title>
<style>
</style>
</head>
<body>
<div id="anElementWithInnerHTML"></div>
<img id="anElementWithInnerSRC" />
<script>
var div = document.getElementById("anElementWithInnerHTML");
console.log(div.innerHTML); //Outputs ""
console.log(div.src); //Outputs undefined
var img = document.getElementById("anElementWithInnerSRC");
console.log(img.innerHTML); //Outputs "" (weird right?)
console.log(img.src); //Outputs ""
</script>
</body>
</html>
I suppose you could do
if (typeof element.src !== 'undefined')
if (element.innerHTML ...)
Not totally reliable since any element could add those properties (JSON).
(thanks for the typeof fix, thief)
I found the list of elements that (according to the spec) allow the src attribute:
audio, embed, iframe, img, input, script, source, track, video
So this works to check by name:
function srcAllowed(tag) {
if ( !tag ) { return false; }
var tags = ['audio','embed','iframe','img','input','script','source','track','video'];
return 0 <= $.inArray(tag.toLowerCase(), tags); // boolean
}
This works for getting the appropriate content:
function getContent(elem) {
// #param elem is a selected element like $(this)
// returns empty string if attr() and html() are both are falsey
return elem.attr('src') || elem.html();
}
And this is even safer:
function getContentSafer(elem) {
// #param elem is a selected element like $(this)
// returns empty string if attr() and html() are both are falsey
return srcAllowed(elem.prop('tagName')) ? (elem.attr('src') || elem.html()) : elem.html();
}

creating html by javascript DOM (realy basic question)

i'm having some trouble with javascript. Somehow i can't get started (or saying i'm not getting any results) with html elements creation by javascript.
i'm not allowed to use:
document.writeln("<h1>...</h1>");
i've tried this:
document.getElementsByTagName('body').appendChild('h1');
document.getElementsByTagName('h1').innerHTML = 'teeeekst';
and this:
var element = document.createElement('h1');
element.appendChild(document.createTextNode('text'));
but my browser isn't showing any text. When i put an alert in this code block, it does show. So i know the code is being reached.
for this school assignment i need to set the entire html, which normally goes into the body, by javascript.
any small working code sample to set a h1 or a div?
my complete code:
<html>
<head>
<title>A boring website</title>
<link rel="stylesheet" type="text/css" href="createDom.css">
<script type="text/javascript">
var element = document.createElement('h1');
element.innerHTML = "Since when?";
document.body.appendChild(element);
</script>
</head>
<body>
</body>
</html>
getElementsByTagName returns a NodeList (which is like an array of elements), not an element. You need to iterate over it, or at least pick an item from it, and access the properties of the elements inside it. (The body element is more easily referenced as document.body though.)
appendChild expects an Node, not a string.
var h1 = document.createElement('h1');
var content = document.createTextNode('text');
h1.appendChild(content);
document.body.appendChild(h1);
You also have to make sure that the code does not run before the body exists as it does in your edited question.
The simplest way to do this is to wrap it in a function that runs onload.
window.onload = function () {
var h1 = document.createElement('h1');
var content = document.createTextNode('text');
h1.appendChild(content);
document.body.appendChild(h1);
}
… but it is generally a better idea to use a library that abstracts the various robust event handling systems in browsers.
Did you append the element to document?
Much the same way you're appending text nodes to the newly created element, you must also append the element to a target element of the DOM.
So for example, if you want to append the new element to a <div id="target"> somewhere are the page, you must first get the element as target and then append.
//where you want the new element to do
var target = document.getElementById('target');
// create the new element
var element = document.createElement('h1');
element.appendChild(document.createTextNode('text'));
// append
target.appendChild(element);
create element, add html content and append to body
var element = document.createElement('h1');
element.innerHTML = 'teeeekst';
document.body.appendChild(element);

document.write() and Ajax - Doesn't work, looking for an alternative

I recently asked a question here, and received a great response (which I will shortly be accepting the most active answer of, barring better alternatives arise) but unfortunately it seems the of the two options suggested, neither will be compatible with Ajax (or any dynamically added content that includes such "inline-relative jQuery")
Anyways, my question pertains to good ole' document.write().
While a page is still rendering, it works great; not so much when an appended snippet contains it. Are there any alternatives that won't destroy the existing page content, yet still append a string inline, as in where the call is occurring?
In other words, is there a way/alternative to document.write() that when called post-render, doesn't destroy existing page content? An Ajax friendly version so to speak?
This is where I'm going:
var _inline_relative_index = 0;
function $_inlineRelative(){
// i hate non-dedicated string concatenation operators
var inline_relative_id = ('_inline_relative_{index}').replace('{index}', (++_inline_relative_index).toString());
document.write(('<br id="{id}" />').replace('{id}', inline_relative_id));
return $(document.getElementById(inline_relative_id)).remove().prev('script');
}
And then:
<div>
<script type="text/javascript">
(function($script){
// the container <div> background is now red.
$script.parent().css({ 'background-color': '#f00' });
})($_inlineRelative());
</script>
</div>
you have access to the innerHTML property of each DOM node. If you set it straight out you might destroy elements, but if you append more HTML to it, it'll preserve the existing HTML.
document.body.innerHTML += '<div id="foo">bar baz</div>';
There are all sorts of nuances to the sledgehammer that is innerHTML, so I highly recommend using a library such as jQuery to normalize everything for you.
You can assign id to the script tag and replace it with the new node.
<p>Foo</p>
<script type="text/javascript" id="placeholder">
var newElement = document.createElement('div');
newElement.id='bar';
var oldElement = document.getElementById('placeholder');
oldElement.parentNode.replaceChild(newElement, oldElement);
</script>
<p>Baz</p>
And if you need to insert html from string, than you can do it like so:
var div = document.createElement('div');
div.innerHTML = '<div id="bar"></div>';
var placeholder = document.getElementById('placeholder'),
container = placeholder.parentNode,
elems = div.childNodes,
el;
while (el = elems[0]) {
div.removeChild(el);
container.insertBefore(el, placeholder);
}
container.removeChild(placeholder);

IE9, Javascript: Create and append a new element

I'm having some serious trouble getting my code to work in IE9, works fine in Chrome & Firefox but I throws some errors. Here are my 2 functions:
function insertHTML(content){
var body=document.getElementsByTagName('body');
body[0].appendChild(createElement(content));
}
function createElement(string){
var container=document.createElement('div');
container.innerHTML=string;
var element=container.firstChild.cloneNode(true);
return element;
}
I've tried severel methods for this and none seem to work, I'll explain exactly what I need to do...
...I need to create a new element from an html string, the string is sent back from an ajax call so my script will have almost no idea what it contains until it gets it.
I did try using element.innerHTML but this is no good, because if i have one html element (form) on the screen and the user enters data into it, and then when another element is inserted it will wipe all the user-entered data from the first form. I was doing element.innerHTML+=newData;
So basically, I need 2 things:
1) A way to create a new element from an html string.
2) A way to append the element to the document body.
It all needs to work cross-browser and I'm not allowed to use jQuery, also the new element cannot be contained in a div parent item, it has to have the body as its parent.
Thanks very much for your help,
Richard
innerHTML is read write and will destroy anything inside your div. use with extreme care
function insertHTML( htmlString ){
var bodEle = document.getElementsByTagName('body');
var divEle = createElement("div");
divEle.innerHTML = htmlString;
bodEle.appendChild(divEle);
}
So basically, I need 2 things:
A way to create a new element from an html string.
A way to append the element to the document body.
It all needs to work cross-browser and I'm not allowed to use jQuery, also the new element cannot be contained in a div parent item, it has to have the body as its parent.
The following was tested in IE8
<!DOCTYPE html>
<html>
<body>
<script>
var divBefore = document.createElement('div');
var divAfter = document.createElement('div');
var htmlBefore = '<span><span style="font-weight: bold">This bold text</span> was added before</span>';
var htmlAfter = '<span><span style="font-weight: bold">This bold text</span> was added after</span>';
divBefore.innerHTML = htmlBefore;
divAfter.innerHTML = htmlAfter;
document.body.appendChild(divBefore);
setTimeout(function() {
document.body.appendChild(divAfter);
}, 0);
</script>
<div>This content was here first</div>
</body>
</html>
Renders
This bold text was added before
This content was here first
This bold text was added after
https://www.browserstack.com/screenshots/7e166dc72b636d3dffdd3739a19ff8956e9cea96
In the above example, if you don't need to be able to prepend to the body (i.e. insert content before what already exists), then simply place the script tag after the original content and don't use setTimeout.
<!DOCTYPE html>
<html>
<body>
<div>This content was here first</div>
<script>
var divAfter = document.createElement('div');
var htmlAfter = '<span><span style="font-weight: bold">This bold text</span> was added after</span>';
divAfter.innerHTML = htmlAfter;
document.body.appendChild(divAfter);
</script>
</body>
</html>

Categories