PDA

View Full Version : DOM Changing element Tag type...



Falkon303
03-01-2009, 01:58 AM
Ok, so I am able to use the DOM method to create/append objects....

But say I wanted to just replace the contents of "bojo" completely (I am talking similar to how innerHTML works).

If I am not mistaken, there is a "destroyChild" function? Is this how it must be done? Any help appreciated.

- Ben



<script type="text/javascript">
function initload(value){
elOpt = document.createElement(value);
elOpt.setAttribute("id","my" + value);
elOpt.style.border = "4px solid #cccccc";
elOpt.style.height = "40px";
elOpt.style.width = "40px";
elOpt.style.margin = "4px";
elOpt.style.display = "inline";
document.getElementById("bojo").appendChild(elOpt);
}
</script>
<body id="body">
<div id="bojo"></div>
<input onClick="initload(this.value);" type="submit" name="Submit" value="div">
<input onClick="initload(this.value);" type="submit" name="Submit" value="a">
<input onClick="initload(this.value);" type="submit" name="Submit" value="hr">
<input onClick="initload(this.value);" type="submit" name="Submit" value="textarea">
<input onClick="initload(this.value);" type="submit" name="Submit" value="li">
<input onClick="initload(this.value);" type="submit" name="Submit" value="p">
<input onClick="initload(this.value);" type="submit" name="Submit" value="ul">
<input onClick="initload(this.value);" type="submit" name="Submit" value="span">
</body>




I found this code (below, not above) at http://www.tek-tips.com/viewthread.cfm?qid=961910&page=1


function destroyDiv() {
obj = document.getElementById("blahDiv");
document.body.removeChild(obj);
}

is document.body good usage?? I'd think they could have used document.formname.removeChild(obj)?

magicyte
03-01-2009, 02:03 AM
There's one way, but it probably won't work unless you change this one function:


function initload(value){
elOpt = document.createElement(value);
elOpt.setAttribute("id","my" + value);
elOpt.style.border = "4px solid #cccccc";
elOpt.style.height = "40px";
elOpt.style.width = "40px";
elOpt.style.margin = "4px";
elOpt.style.display = "inline";
document.getElementById("bojo").appendChild(elOpt);
}

I think that the highlighted code should have a counter. You'll need to be careful because you could have more than one element with the same id... Also, I think you should change the types of the submit buttons to button.

Saw edit...

magicyte
03-01-2009, 02:12 AM
It's good usage, I guess. Here's a better version:


function destroyDiv(elem) {
var el = document.getElementById(elem);
document.getElementById("bojo").removeChild(el);
}

Remember: to make this stuff more compatible, you should make a counter variable for elOpt.setAttribute("id","my" + value);

Falkon303
03-01-2009, 02:22 AM
Word, thanks gents...

currently I am using

function remch()
{document.getElementById('bojo').removeChild(elOpt);}

This works, considering elOpt exists. I always forget that freaking function for verifying if an element exists... You guys recall?

You know... Dynamic drive should have a snippet library where if a snippet is wrong or something it can be put on hold by admins, but it's so often I find myself needing a simple function but I just can't quite remember it...

check if element exists =

if (document.getElementById('bojo').firstChild.id == undefined)
{}

UPDATE:

Got it...


function remch() {
if (document.getElementById("bojo").firstChild == null) {
;} else {
document.getElementById("bojo").removeChild(elOpt);
}
}

Thanx for the help guys. :)

vwphillips
03-01-2009, 11:14 AM
function initload(value){
var obj=document.getElementById("bojo");
while (obj.firstChild) obj.removeChild(obj.firstChild);
elOpt = document.createElement(value);
var elOpt.setAttribute("id","my" + value);
elOpt.style.border = "4px solid #cccccc";
elOpt.style.height = "40px";
elOpt.style.width = "40px";
elOpt.style.margin = "4px";
elOpt.style.display = "inline";
obj.appendChild(elOpt);
}

Falkon303
03-01-2009, 01:24 PM
function initload(value){
var obj=document.getElementById("bojo");
while (obj.firstChild) obj.removeChild(obj.firstChild);
elOpt = document.createElement(value);
var elOpt.setAttribute("id","my" + value);
elOpt.style.border = "4px solid #cccccc";
elOpt.style.height = "40px";
elOpt.style.width = "40px";
elOpt.style.margin = "4px";
elOpt.style.display = "inline";
obj.appendChild(elOpt);
}



It didn't work for me... I get what you are saying though. Create the element on start, and destroy it every function call.

Isn't there any way to change the tag type using Dom? That would be so useful.

My issue is this - If I have a div, and I want to change it's tag type to an 'li", and the div is FULL of elements, is it possible to easily change the element type without getting every element within it? If there is a way to convert text to html through javascript, I may have a solution....

Twey
03-01-2009, 07:32 PM
var elOpt.setAttribute("id","my" + value);elOpt.setAttribute("id","my" + value) is not a valid identifier.

No, Falkon303: the element type is intrinsic to the element. Once defined at creation time, it cannot be changed — what would happen to attributes and children that were no longer valid for that element?

You can create a new element and then attempt to transfer the children of the old one:


function changeElementType(el, to) {
var newEl = document.createElement(to);

// Try to copy attributes across
for (var i = 0, a = el.attributes, n = a.length; i < n; ++i)
oldEl.setAttribute(a[i].name, a[i].value);

// Try to move children across
while (el.hasChildNodes())
newEl.appendChild(el.firstChild);

// Replace the old element with the new one
el.parentNode.replaceChild(newEl, oldEl);

// Return the new element, for good measure.
return newEl;
}For performance reasons, this is destructive; for flexibility reasons, it does not handle possible errors (if something goes wrong in the transfer, an exception will be thrown).

Falkon303
11-24-2009, 11:47 PM
elOpt.setAttribute("id","my" + value) is not a valid identifier.

No, Falkon303: the element type is intrinsic to the element. Once defined at creation time, it cannot be changed — what would happen to attributes and children that were no longer valid for that element?

You can create a new element and then attempt to transfer the children of the old one:


function changeElementType(el, to) {
var newEl = document.createElement(to);

// Try to copy attributes across
for (var i = 0, a = el.attributes, n = a.length; i < n; ++i)
oldEl.setAttribute(a[i].name, a[i].value);

// Try to move children across
while (el.hasChildNodes())
newEl.appendChild(el.firstChild);

// Replace the old element with the new one
el.parentNode.replaceChild(newEl, oldEl);

// Return the new element, for good measure.
return newEl;
}For performance reasons, this is destructive; for flexibility reasons, it does not handle possible errors (if something goes wrong in the transfer, an exception will be thrown).

Very nice indeed Twey,

I am curious if there is a library for transferring elements from>to others.

I think it would be possible to write a recursive script that would check the depth of each sub element until all of the properties had been transferred correctly. It makes sense also to avoid errors by using something such as "if(a[i].name)" I'd hope there be a simple "a.nodeTree" method, but alas!!

jscheuer1
11-25-2009, 04:15 AM
jQuery, probably other script libraries, can fetch all the children of an element and then (code wise - I can't really say as far as execution time/CPU resources go) efficiently analyze these children for whatever. But the crux of the issue is determining what 'whatever' should be.

If this is just for you and you know what the children will typically be, this is not a big issue. But in terms of portable code that anyone could use in any situation, it is (as Twey intimates) at the very least a small nightmare.

So let's say this is just for you and you want to change something about a file input, most if not all of which is prohibited for security reasons. You could copy everything conceivable about the file input, or (depending upon the circumstances) simply clone it, and set some stuff for the clone or the new element of a different type you are making to replace it, and then replace the original file input with the new element. File inputs cannot have any children. So at least on that score, things would be less complicated.

Now, I haven't read in detail all of this thread. Perhaps if you would tell us some precise thing you want to do as regards a particular element on a page of yours (apologies if this was mentioned and I missed it), we could possibly help you to achieve that.

Falkon303
11-28-2009, 06:47 AM
jQuery, probably other script libraries, can fetch all the children of an element and then (code wise - I can't really say as far as execution time/CPU resources go) efficiently analyze these children for whatever. But the crux of the issue is determining what 'whatever' should be.

If this is just for you and you know what the children will typically be, this is not a big issue. But in terms of portable code that anyone could use in any situation, it is (as Twey intimates) at the very least a small nightmare.

So let's say this is just for you and you want to change something about a file input, most if not all of which is prohibited for security reasons. You could copy everything conceivable about the file input, or (depending upon the circumstances) simply clone it, and set some stuff for the clone or the new element of a different type you are making to replace it, and then replace the original file input with the new element. File inputs cannot have any children. So at least on that score, things would be less complicated.

Now, I haven't read in detail all of this thread. Perhaps if you would tell us some precise thing you want to do as regards a particular element on a page of yours (apologies if this was mentioned and I missed it), we could possibly help you to achieve that.

I am just curious as to how it'd be done is all.

I am thinking it'd be best to use a multidimensional array, to title each array as each child element, and for each array value's key to hold the name of each attribute, and each array value to hold the attributes value..

Where I am foggy is the method of traversing the DOM.

so basically something like -


1. while traversing DOM, write each element as a multidimenional array (ie: array[a], array[span].
2. for each amultidimensional array, throw in whatever attributes are found based on the attribute array. include "firstChild.nodeValue" of course.
3. once this multidimensional array is made, document.appendChild this using createElement, and Element.src/id/j = (value)

This would work great for getting attributes -



<script language="JavaScript">
<!--
function attribs()
{
attribs = document.getElementById('ael').attributes;
for (a=0; a<attribs.length; a++)
{
if (attribs[a].value && attribs[a].value !== "null")
{
alert(attribs[a].nodeName + ':' + attribs[a].value);
}
}
}
//-->
</script>

<a id="ael" name="ael" href="javascript:attribs()">Get Attributes</a>


It seems the problem being that if the text "Get Attributes" was followed by "<br>because attributes are neat", firstChild.nodeValue is cut short... When I get some sleep and coffee, I'll think about this more.

jscheuer1
11-28-2009, 10:35 AM
<a id="ael" name="ael" href="javascript:attribs()">Get Attributes<br>because attributes are neat</a>

In the above a element, there are three children:


Get Attributes - firstChild, a text node

<br> - childNodes[1], a br element

because attributes are neat - lastChild, a text node


See also:

http://www.howtocreate.co.uk/tutorials/javascript/dombasics

Falkon303
12-01-2009, 12:37 AM
I attempted to create something of what I was trying to accomplish. For whatever the reason though, it's not getting all of the attributes, and the structure is horribly off... I know there is a simple way to accomplish this, I just haven't quite gotten it yet.



<script language="JavaScript">
<!--
function elements()
{
var tag, tags;
for(i = 0; i < document.all.length; i++)
{
tag = document.all(i).tagName;

nodes = document.all(i).childNodes;
for (a=0; a<nodes.length; a++)
{nds = Array();
if (nodes[a].nodeValue && nodes[a].nodeValue !== "null")
{nds[a] = nodes[a].nodeValue;}
}

atts = document.all(i).attributes;
for (b=0; b<atts.length; b++)
{att = Array();
if (atts[b].nodeValue > '' && atts[b].nodeValue !== "null")
{att[b] = atts[b].nodeName + '=' + atts[b].nodeValue;}
}

tags = tags + '::' + att + ":" + nds + "\r" + tag;
}

alert(tags);
}

// -->
</script>
<a id="ael" name="ael" onclick="elements()">
Get Attributes<div id="1" name="1">div text here<a>a text here</a><div>second div text here<br>bleh</div></div>
</a>

jscheuer1
12-01-2009, 03:48 AM
I'm not exactly clear on what you are going for. And your HTML code is odd, with a nested a tag, not a good idea in actual practice, invalid (as far as I know) according to standards. Every element in a document has more attributes than you can imagine, and these vary depending upon the browser, and upon if certain script libraries are in effect and/or have been applied to an element or elements on the page. Anyways, an alert may not be big enough. I've used a textarea:


<script type="text/javascript">
function elements(node){
node = node || document.body;
var nodes = node.childNodes, atts, p;
for(var i = 0; i < nodes.length; ++i){
if(nodes[i] !== elements.ta){
atts = nodes[i].attributes;
elements.info += nodes[i].nodeName + ':\n';
if(atts){
for(p in atts){
elements.info += p + ': ' + atts[p] + '\n';
}
}
else{
elements.info += 'value: ' + nodes[i].nodeValue.replace(/\n/g, '') + '\n';
}
elements.info += '\n';
if(nodes[i].childNodes){
elements(nodes[i]);
}
}
}
if(!elements.ta){
elements.ta = document.createElement('textarea');
elements.ta.rows = 22;
elements.ta.cols = 60;
document.body.appendChild(elements.ta);
}
elements.ta.value = elements.info;
}
elements.info = '';
</script>
<a onclick="elements()">
Get Attributes<div id="1" name="1">div text here<a>a text here</a><div>second div text here<br>bleh</div></div>
</a>

But most, if not all of this information is available via the optional Firefox DOM inspector, or the optional IE (7+) developer tool bar. Most other browsers have either built in equivalent tools or optional ones for this as well.

Falkon303
12-01-2009, 06:03 PM
I'm not exactly clear on what you are going for. And your HTML code is odd, with a nested a tag, not a good idea in actual practice, invalid (as far as I know) according to standards. Every element in a document has more attributes than you can imagine, and these vary depending upon the browser, and upon if certain script libraries are in effect and/or have been applied to an element or elements on the page. Anyways, an alert may not be big enough. I've used a textarea:


<script type="text/javascript">
function elements(node){
node = node || document.body;
var nodes = node.childNodes, atts, p;
for(var i = 0; i < nodes.length; ++i){
if(nodes[i] !== elements.ta){
atts = nodes[i].attributes;
elements.info += nodes[i].nodeName + ':\n';
if(atts){
for(p in atts){
elements.info += p + ': ' + atts[p] + '\n';
}
}
else{
elements.info += 'value: ' + nodes[i].nodeValue.replace(/\n/g, '') + '\n';
}
elements.info += '\n';
if(nodes[i].childNodes){
elements(nodes[i]);
}
}
}
if(!elements.ta){
elements.ta = document.createElement('textarea');
elements.ta.rows = 22;
elements.ta.cols = 60;
document.body.appendChild(elements.ta);
}
elements.ta.value = elements.info;
}
elements.info = '';
</script>
<a onclick="elements()">
Get Attributes<div id="1" name="1">div text here<a>a text here</a><div>second div text here<br>bleh</div></div>
</a>

But most, if not all of this information is available via the optional Firefox DOM inspector, or the optional IE (7+) developer tool bar. Most other browsers have either built in equivalent tools or optional ones for this as well.

You coded what I was going for actually. I haven't dug that far into loops yet. I don't know how "for(p in atts)" actually works, as I'm mainly familiar with foreach, for, and while loops.

I appreciate the great response, and I had the chance to try out this code as well. It's very cool.

What I am trying to accomplish involves sortables, and being able to drag elements into other elements. I am guessing this is what is used in the javascript WYSIWYG editors as well?