IE9, Javascript: Create and append a new element - javascript

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>

Related

Alternatives to document.write

I am in a situation where it seems that I must use document.write in a javascript library. The script must know the width of the area where the script is defined. However, the script does not have any explicit knowledge of any tags in that area. If there were explicit knowledge of a div then it would be as simple as this:
<div id="childAnchor"></div>
<script ref...
//inside of referenced script
var divWidth = $("#childAnchor").width();
</script>
So, inside of the referenced script, I am thinking of doing using document.write like this:
<script ref...
//inside of referenced script
var childAnchor = "z_87127XNA_2451ap";
document.write('<div id="' + childAnchor + '"></div>');
var divWidth = $("#" + childAnchor).width();
</script>
However, I do not really like the document.write implementation. Is there any alternative to using document.write here? The reason that I cannot simply use window is that this is inside of a view which is rendered inside of a master view page. Window would not properly get the nested area width.
The area is pretty much in here:
<body>
<div>
<div>
<div>
AREA
The AREA has no knowledge of any of the other divs.
This just occurred to me, and I remembered your question: the script code can find the script block it is in, so you can traverse the DOM from there. The current script block will be the last one in the DOM at the moment (the DOM still being parsed when the code runs).
By locating the current script block, you can find its parent element, and add new elements anywhere:
<!doctype html>
<html>
<head>
<style>
.parent .before { color: red; }
.parent .after { color: blue; }
</style>
</head>
<body>
<script></script>
<div class="parent">
<span>before script block</span>
<script>
var s = document.getElementsByTagName('script');
var here = s[s.length-1];
var red = document.createElement("p");
red.className = 'before';
red.innerHTML = "red text";
here.parentNode.insertBefore(red, here);
var blue = document.createElement("p");
blue.className = 'after';
blue.innerHTML = "blue text";
here.parentNode.appendChild(blue);
</script>
<span>after script block</span>
</div>
<script></script>
</body>
</html>​
http://jsfiddle.net/gFyK9/2/
Note the "blue text" span will be inserted before the "after script block" span. That's because the "after" span does not exist in the DOM at the moment appendChild is called.
However there's a very simple way to make this fail.
There is no alternative to this method. This is the only way that the script can find the width of the element that it is nested in without knowing anything about the DOM that it is loaded into.
Using document.write() when used appropriately is legitimate. Although, appending a new element to the DOM is preferable, it is not always an available option.
if you know which child element it is you can use the param nth child in jquery.
otherwise youll need to iterate through them with each()
Create and append the child like this:
var el = document.createElement('div');
el.id='id';
document.body.appendChild(el);
However I'm not really sure what you want to do with this to get the width of whatever, it will probably return 0.

JS - Remove a tag without deleting content

I am wondering if it is possible to remove a tag but leave the content in tact? For example, is it possible to remove the SPAN tag but leave SPAN's content there?
<p>The weather is sure <span>sunny</span> today</p> //original
<p>The weather is sure sunny today</p> //turn it into this
I have tried using this method of using replaceWith(), but it it turned the HTML into
<p>
"The weather is sure "
"sunny"
" today"
</p>
EDIT : After testing all of your answers, I realized that my code is at fault. The reason why I keep getting three split text nodes is due to the insertion of the SPAN tag. I'll create another question to try to fix my problem.
<p>The weather is sure <span>sunny</span> today</p>;
var span=document.getElementsByTagName('span')[0]; // get the span
var pa=span.parentNode;
while(span.firstChild) pa.insertBefore(span.firstChild, span);
pa.removeChild(span);
jQuery has easier ways:
var spans = $('span');
spans.contents().unwrap();
With different selector methods, it is possible to remove deeply nested spans or just direct children spans of an element.
There are several ways to do it. Jquery is the most easy way:
//grab and store inner span html
var content = $('p span').html;
//"Re"set inner p html
$('p').html(content);
Javascript can do the same using element.replace. (I don't remember the regex to do the replace in one stroke, but this is the easy way)
paragraphElement.replace("<span>", "");
paragraphElement.replace("</span>", "");
It's just three text nodes instead of one. It doesn't make a visible difference does it?
If it's a problem, use the DOM normalize method to combine them:
$(...)[0].normalize();
$(function(){
var newLbl=$("p").clone().find("span").remove().end().html();
alert(newLbl);
});​
Example : http://jsfiddle.net/7gWdM/6/
If you're not looking for a jQuery solution, here something that's a little more lightweight and focused on your scenario.
I created a function called getText() and I used it recursively. In short, you can get the child nodes of your p element and retrieve all the text nodes within that p node.
Just about everything in the DOM is a node of some sort. Looking up at the following links I found that text nodes have a numerical nodeType value of 3, and when you identify where your text nodes are, you get their nodeValueand return it to be concatenated to the entire, non-text-node-free value.
https://developer.mozilla.org/en/nodeType
https://developer.mozilla.org/En/DOM/Node.nodeValue
var para = document.getElementById('p1') // get your paragraphe
var texttext = getText(para); // pass the paragraph to the function
para.innerHTML = texttext // set the paragraph with the new text
function getText(pNode) {
if (pNode.nodeType == 3) return pNode.nodeValue;
var pNodes = pNode.childNodes // get the child nodes of the passed element
var nLen = pNodes.length // count how many there are
var text = "";
for (var idx=0; idx < nLen; idx++) { // loop through the child nodes
if (pNodes[idx].nodeType != 3 ) { // if the child not isn't a text node
text += getText(pNodes[idx]); // pass it to the function again and
// concatenate it's value to your text string
} else {
text += pNodes[idx].nodeValue // otherwise concatenate the value of the text
// to the entire text
}
}
return text
}
I haven't tested this for all scenarios, but it will do for what you're doing at the moment. It's a little more complex than a replace string since you're looking for the text node and not hardcoding to remove specific tags.
Good Luck.
If someone is still looking for that, the complete solution that has worked for me is:
Assuming we have:
<p>hello this is the <span class="highlight">text to unwrap</span></p>
the js is:
// get the parent
var parentElem = $(".highlight").parent();
// replacing with the same contents
$(".highlight").replaceWith(
function() {
return $(this).contents();
}
);
// normalize parent to strip extra text nodes
parentElem.each(function(element,index){
$(this)[0].normalize();
});
If it’s the only child span inside the parent, you could do something like this:
HTML:
<p class="parent">The weather is sure <span>sunny</span> today</p>;
JavaScript:
parent = document.querySelector('.parent');
parent.innerHTML = parent.innerText;
So just replace the HTML of the element with its text.
You can remove the span element and keep the HTML content or internal text intact. With jQuery’s unwrap() method.
<html>
<head>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$("button").click(function(){
$("p").find("span").contents().unwrap();
});
});
</script>
</head>
<body>
<p>The weather is sure <span style="background-color:blue">sunny</span> today</p>
<button type="button">Remove span</button>
</body>
</html>
You can see an example here: How to remove a tag without deleting its content with jQuery

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);

Get all html between two elements

Problem:
Extract all html between two headers including the headers html. The header text is known, but not the formatting, tag name, etc. They are not within the same parent and might (well, almost for sure) have sub children within it's own children).
To clarify: headers could be inside a <h1> or <div> or any other tag. They may also be surrounded by <b>, <i>, <font> or more <div> tags. The key is: the only text within the element is the header text.
The tools I have available are: C# 3.0 utilizing a WebBrowser control, or Jquery/Js.
I've taken the Jquery route, traversing the DOM, but I've ran into the issue of children and adding them appropriately. Here is the code so far:
function getAllBetween(firstEl,lastEl) {
var collection = new Array(); // Collection of Elements
var fefound =false;
$('body').find('*').each(function(){
var curEl = $(this);
if($(curEl).text() == firstEl)
fefound=true;
if($(curEl).text() == lastEl)
return false;
// need something to add children children
// otherwise we get <table></table><tbody></tbody><tr></tr> etc
if (fefound)
collection.push(curEl);
});
var div = document.createElement("DIV");
for (var i=0,len=collection.length;i<len;i++){
$(div).append(collection[i]);
}
return($(div).html());
}
Should I be continueing down this road? With some sort of recursive function checking/handling children, or would a whole new approach be better suited?
For the sake of testing, here is some sample markup:
<body>
<div>
<div>Start</div>
<table><tbody><tr><td>Oops</td></tr></tbody></table>
</div>
<div>
<div>End</div>
</div>
</body>
Any suggestions or thoughts are greatly appreciated!
My thought is a regex, something along the lines of
.*<(?<tag>.+)>Start</\1>(?<found_data>.+)<\1>End</\1>.*
should get you everything between the Start and end div tags.
Here's an idea:
$(function() {
// Get the parent div start is in:
var $elie = $("div:contains(Start)").eq(0), htmlArr = [];
// Push HTML of that div to the HTML array
htmlArr.push($('<div>').append( $elie.clone() ).html());
// Keep moving along and adding to array until we hit END
while($elie.find("div:contains(End)").length != 1) {
$elie = $elie.next();
htmlArr.push($('<div>').append( $elie.clone() ).html());
};
// htmlArr now has the HTML
// let's see what it is:
alert(htmlArr.join(""));
});​
Try it out with this jsFiddle example
This takes the entire parent div that start is in. I'm not sure that's what you want though. The outerHTML is done by $('<div>').append( element.clone() ).html(), since outerHTML support is not cross browser yet. All the html is stored in an array, you could also just store the elements in the array.

Categories