2

This is the same question as this:

Referring to a div inside a div with the same ID as another inside another

except for one thing.

The reason there are two elements with the same ID is because I'm adding rows to a table, and I'm doing that by making a hidden div with the contents of the row as a template. I make a new div, copy the innerhtml of the template to my new div, and then I just want to edit bits of it, but all the bits have the same ID as the template.

I could dynamically create the row element by element but it's a VERY complex row, and there's only a few things that need to be changed, so it's a lot easier to just copy from a template and change the few things I need to.

So how do I refer to the elements in my copy, rather than the template? I don't want to mess up the template itself, or I'll never be able to get at the bits for a second use.

Or is there another simpler way to solve the problem?

Community
  • 1
  • 1
stu
  • 8,461
  • 18
  • 74
  • 112
  • The "id" attributes really need to be unique; that's why it's called an "id" ("identity"). Your template, therefore, shouldn't have elements with "id" values. You can always make "id" values with template variables of course. – Pointy Dec 08 '11 at 16:56
  • what do you mean by template variables? Google doesn't answer me that. – stu Dec 08 '11 at 17:29
  • Well if you're using a templating system, it (probably) allows some data to be bound in when the template is expanded. If by "template" you just mean "block of stuff to copy", then never mind :-) – Pointy Dec 08 '11 at 17:37
  • It would be helpful if you showed the code that you're currently using in your question. – Brian Kelly Dec 23 '11 at 18:10

6 Answers6

1

IDs on a page are supposed to be unique, even when you clone them from a template.

If you dynamically create content on your page, then you must change the id of your newly cloned elements to something else. If you want to access all cloned elements, but not the template, you can add a class to them, so you can refer to all elements with that class:

var clonedElement = template.cloneNode(yes); // make a deep copy
clonedElement.setAttribute("id", "somethingElse"); // change the id
clonedElement.setAttribute("class", 
  clonedElement.getAttribute("class") + " cloned"
);

To access all cloned elements by classname, you can use the getElementsByClassName method (available in newer browsers) or look at this answer for a more in-depth solution: How to getElementByClass instead of GetElementById with Javascript?

Alternatively, if you have jQuery available, you can do this is far less lines of code:

$("#template").clone().attr("id","somethingElse")
  .addClass("cloned").appendTo("#someDiv");

The class lookup is even simpler:

$(".cloned").doSomethingWithTheseElements();

Try to avoid using IDs in the child elements of the cloned structure, as all ids of the cloned element should be changed before adding the clone to the page. Instead, you can refer to the parent element using the new id and traverse the rest of the structure using classnames. Class names do not need to be unique, so you can just leave them as they are.

If you really must use ID's (or unique "name" attributes in form fields), I can strongly suggest using a framework like jQuery or Prototype to handle the DOM traversal; otherwise, it is quite a burden to resolve all the cross-browser issues. Here is an example of some changes deeper in the structure, using jQuery:

$("#template").clone().attr("id","somethingElse")
  .addClass("cloned") // add a cloned class to the top element
  .find("#foo").attr("id","bar").end() // find and modify a child element
  .appendTo("#someDiv"); // finally, add the node to the page
Community
  • 1
  • 1
Scharrels
  • 3,055
  • 25
  • 31
  • I get the concept of what you're saying, but I don't see how I can change the class or id in the clone, because I can't find them. The clone will have the same classnames too. Your example shows changing the class of the cloned element which would be the parent, but there are LOTS of other nested nodes that I'd also have to change the class or id of, how do I get at those? – stu Dec 08 '11 at 17:35
  • 1
    The general idea is to modify the cloned element before adding it to the page. Cloning a DOM element in javascript returns a reference to the cloned element. Using getElementsByClassName and similar methods, you can traverse this element and modify it before adding it to the page. It is very easy to encounter a cross-browser issue if you do this in plain javascript. I can therefore highly recommend using a framework if you need to modify a lot of elements. I've added an example for such a modification to my answer. – Scharrels Dec 11 '11 at 22:57
1

It will probably just be easiest when manipulating the innerHtml to do a replace on the IDs for that row. Maybe something like...

var copiedRow = templateRow.innerHTML.replace(/id=/g,"$1copy")

This will make the copied divs be prefixed with "copy". You can develop this further for the case that you have multiple copies by keeping a counter and adding that count variable to the replace() call.

king14nyr
  • 715
  • 6
  • 21
1

When you want to make a template and use it multiple times its best to make it of DOM, in a documentFragment for example. That way it doesn't respond to document.getElementById() calls in the "live" DOM. I made an example here: http://jsfiddle.net/PM5544/MXHRr/

id's should be unique on the page.

PM5544...

PM5544
  • 700
  • 4
  • 5
1

In reality, there's no use to change the ID to something unique, even though your document may not be valid.

Browsers' selector engines treat IDs pretty much the same as class names. Thus, you may use

document.querySelector('#myCopy #idToLookFor');

to get the copy.

user123444555621
  • 148,182
  • 27
  • 114
  • 126
0

Check out my ugly but functional cheese. I wrote a function that works like getelementbyid, but you give it a start node instead of the document. Works like a charm. It may be inefficient but I have great faith in the microprocessors running today's browsers' javascript engines.

function getelement(node, findid) 
  {
    if (node)
      if (node.id)
        if (node.id == findid)
          return node;
    node = node.firstChild;
    while(node) 
      {
        var r = getelement(node, findid);
        if (r != null)
          return r;
        node = node.nextSibling;
      }
    return null;
  }
stu
  • 8,461
  • 18
  • 74
  • 112
  • After getting it working, I spent the day fighting with IE because it's picky about using innerHTML to change the definition of a table. I found lots of people with the same problem, most suggestions didn't help. The most important is the trs are children of tbody not table. – stu Dec 09 '11 at 22:10
  • What I found out the hard way myself, is that you can't tr.innerHTML = 'blah' you have to createelement tr AND createelement td, appendchild the td to the tr, then td.innerHTML='blah'; that's the only thing I got to work in IE. Hope it helps somebody. – stu Dec 09 '11 at 22:11
0

When you copy the row, don't you end up having a reference to it? At that point can't you change the ID?

Brian Kelly
  • 5,564
  • 4
  • 27
  • 31
  • I needed the stuff IN the row, and all the stuff in the row had the same IDs as the row I copied it from. – stu Dec 20 '11 at 13:53
  • Well, what works nicely in firefox and chrome is to take the innerHTML of the TR and shove it in a new TR I've added to the table. IE doesn't buy it because it doesn't validate even though it should. So what I ended up doing was make a chunk of javascript innerHTMLs of the TD stuff and I can programaticaly add TRs and TDs to the table, and stuff the innerhtmls into the TDs. Not pretty but it works and is easier than programmatically making all the DOM bits in all the TDs. – stu Dec 23 '11 at 16:30
  • Why not use something like JQuery to do this then? Then you'd be doing actual dom manipulation and you'd be alter to alter the ID of the element once you have a reference to it. – Brian Kelly Dec 23 '11 at 17:07