Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I use duplicate IDs on multiple <template> fragments?

Why does HTML5 validation fail when having duplicate element IDs but on different <template>. I'm planning to use only one template at a time so the actual DOM ID won't be duplicated.

something like:

<template id="companyAccount">
   <li><label>Company: <input type="text" id="account_name"></label></li>
   <li><label>Street: <input id="account_street" ...
   ...
</template>
<template id="residentialAccount">
   <li><label>Name: <input type="text" id="account_name"></label></li>
   <li><label>Street: <input id="account_street" ...
   ...
</template>
<script>
...
let template = $(isResidential ? '#residentialAccount' : '#companyAccount').get(0).content;
$('#account_info').empty().append(template.cloneNode(true));
</script>
like image 876
stoic little Avatar asked Dec 02 '25 10:12

stoic little


1 Answers

As TJBlackman mentioned each value for id attributes must be unique. However when working with code it's up to you to determine if the code being imported contains a duplicate id. Additionally I've pretty much stopped using validators for the most part as they aren't well maintained (and the W3C's CSS validator is atrocious) and the consoles, proper error handling and using the XML parser for HTML5 will tell you pretty much everything you need to know.

You have a couple of options. You can use data-account="residential" (don't make the mistake of using camelCasing as that will eventually put you in direct conflict with the standards bodies) and detect the attribute via document.querySelectorAll:

function $(o)
{
 var a = true;

 try {document.querySelectorAll(o);}
 catch(err) {a = false; console.log('Error: "'+o+'" is not a valid CSS selector.'); sound.notice();}

 return (a && document.querySelectorAll && document.querySelectorAll(o)) ? document.querySelectorAll(o) : false;
}

Usage: $('[data-account="residential"]')[0].length and $('[data-account="residential"]')[0].value.

Alternatively you can do what I do with my platform which has things tightly integrated though this is the function I use that you might decide to slim down for your specific purposes. You can use document.createTreeWalker and literally go through every single element to scan for walker.currentNode.hasAttribute('id') to test against document.getElementById. This code is used before importing XML in to the DOM on my platform:

/******** part of larger ajax() function ********/

if (xhr.readyState == 4 && xhr.status != 204)
{}

//This code occurs within the above condition.
var r = jax_id_duplication_prevention(xhr.responseXML,param_id_container_pos,id_container);

if (r)
{
 if (param_id_container_pos=='after') {id_container.parentNode.insertBefore(xml.getElementsByTagName('*')[0],id_container.nextSibling);}
 else if (param_id_container_pos=='before') {id_container.parentNode.insertBefore(document.importNode(xml.getElementsByTagName('*')[0],true),id_container);}
 else if (param_id_container_pos=='first')
 {
  if (id_container.childNodes.length > 0) {id_container.insertBefore(document.importNode(xml.getElementsByTagName('*')[0],true),id_container.firstChild);}
  else {id_container.appendChild(document.importNode(xml.getElementsByTagName('*')[0],true));}
 }
 else if (param_id_container_pos=='inside') {id_container.appendChild(document.importNode(xml.getElementsByTagName('*')[0],true));}
 else if (param_id_container_pos=='replace') {id_container.parentNode.replaceChild(document.importNode(xml.getElementsByTagName('*')[0],true),id_container);}
 else if (param_id_container_pos=='fragment')
 {
  if (option.fragment) {delete option.fragment;}
  option.fragment = document.importNode(new DOMParser().parseFromString(xhr.responseText,'application/xml').childNodes[0],true);
  if (id_container && typeof id_container == 'function') {id_container();}
 }
 else {alert('Error: unknown position to import data to: '+id_container_pos);}
}
/******** part of larger ajax() function ********/

function ajax_id_duplication_prevention(xml,param_id_container_pos,id_container)
{
 var re = true;
 if (typeof id_container == 'string' && id_container.length > 0 && id_(id_container)) {id_container = id_(id_container);}

 if (typeof option.id_fade == 'string' && option.id_fade.length > 0 && id_(option.id_fade)) {element_del(option.id_fade); option.id_fade = '';}

 if (typeof xml.firstChild.hasAttribute == 'function')
 {
  if (xml.firstChild.hasAttribute('id') && xml.firstChild.getAttribute('id').length > 0 && id_(xml.firstChild.getAttribute('id')) && id_(xml.firstChild.id).parentNode.id=='liquid') {change(xml.firstChild.id,'fade');}

  if (xml.firstChild.hasAttribute('id') && xml.firstChild.getAttribute('id').length > 0 && id_(xml.firstChild.id) && !id_(xml.firstChild.id).parentNode.id=='liquid') {re = false;}
  else if (typeof document.createTreeWalker=='function')
  {
   var idz = [];
   try
   {
    var walker = document.createTreeWalker(xml,NodeFilter.SHOW_ELEMENT,null,false);

    while (walker.nextNode())
    {
     if (walker.currentNode.hasAttribute('id') && walker.currentNode.getAttribute('id').length > 0)
     {
      if (walker.currentNode.id==undefined && walker.currentNode.nodeName.toLowerCase()=='parsererror') {console.log('Error: a parser error was detected.');}
      else if (walker.currentNode.id==undefined) {alert('walker.currentNode.nodeName = '+walker.currentNode.nodeName+'\n\n'+document.serializeToString(xml));}
      else
      {
       for (var i = 0; i<id_('liquid').childNodes.length; i++)
       {
        if (id_('liquid').childNodes[i].nodeType==1 && id_(walker.currentNode.id) && is_node_parent(walker.currentNode.id,id_('liquid').childNodes[i]) && (param_id_container_pos!='replace' || walker.currentNode.id!=id_container.id))
        {
         if (param_id_container_pos != 'replace' && id_container != walker.currentNode.id) {element_del(id_('liquid').childNodes[i]);}//If changing operator test: ajax('get','?ajax=1&web3_url=/'+url_section()+'/'+url_page(),'replace',push_current_id());
        }
       }

       var n = id_(walker.currentNode.id);
       if (in_array(walker.currentNode.id,idz))
       {
        var fd = new FormData();
        fd.append('ajax','error_xml');
        fd.append('post_error','Duplicate id <code>'+walker.currentNode.id+'</code>.');
        fd.append('post_url',url_window().split(url_base())[1].split('?')[0]);
        fd.append('post_xml',new XMLSerializer().serializeToString(xml));
        if (fd) {ajax('post',path+'/themes/',fd);}

        modal.alert('Error: can not import XML, the id \''+walker.currentNode.id+'\' was detected twice in the layer being imported. Duplicated ID\'s break expected functionality and are illegal. While the XML content was not imported it is still possible that the related request was successful. It is possible to override this problem by simply doing a full request (press the Go button in your browser\'s graphic user interface) however if the id is referenced programmatically the website may exhibit unusual behavior.');
        break;
        setTimeout(function()
        {
         history.back();
         push_reload();
         console.log('Developer: duplicate id '+walker.currentNode.id+' was encounterted.');
         if (status >= 9) {modal.xml('Duplicate ID Error', '%3Cp%3EError%3A%20the%20id%20%3Ccode%3E'+walker.currentNode.id+'%3C%2Fcode%3E%20occurred%20twice%20and%20therefore%20the%20page%20can%20not%20be%20viewed.%3C%2Fp%3E%3Cp%3EFor%20debugging%20and%20fixing%20purposes%20you%20should%20consider%20opening%20the%20URL%20in%20a%20new%20tab.%3C%2Fp%3E%3Cdiv%20class%3D%22center%20margin%22%3E%3Cinput%20onclick%3D%22modal.close()%3B%22%20tabindex%3D%223%22%20type%3D%22button%22%20value%3D%22Close%22%20%2F%3E%3C%2Fdiv%3E');}
         else {modal.xml('Duplicate ID Error', '%3Cp%3EError%3A%20the%20id%20%3Ccode%3E'+walker.currentNode.id+'%3C%2Fcode%3E%20occurred%20twice%20and%20therefore%20the%20page%20can%20not%20be%20viewed.%3C%2Fp%3E%3Cdiv%20class%3D%22center%20margin%22%3E%3Cinput%20onclick%3D%22modal.close()%3B%22%20tabindex%3D%223%22%20type%3D%22button%22%20value%3D%22Close%22%20%2F%3E%3C%2Fdiv%3E');}
         re = false;
        },4000);
       }
       else {idz.push(walker.currentNode.id);}
      }

      if (id_(walker.currentNode.id) && (param_id_container_pos!='replace' && walker.currentNode.id!=id_container.id && !is_node_parent(walker.currentNode.id,id_container)))
      {//ajax replace (carousel loader) complications if changed.
       re = false;
       modal.alert('Error: unable to import page, the id \''+walker.currentNode.id+'\' already exists in the DOM.');
       break;
      }
     }
    }
   }
   catch (err) {}//IE9
  }
 }

 return re;
}

Regardless of how you approach addressing the issue it is not one addressed by the standards bodies and must be explicitly handled by developers. Failure to handle duplicate id attribute/values will result in the wrong element being chosen at some point which may quietly accrue compromised/malformed data over time that no one might notice for years and thus could easily hinder if not outright destroy any business relations effected by it. Good luck!

like image 181
John Avatar answered Dec 05 '25 01:12

John



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!