Inserting new DOM nodes breaks tabbing, how do I make it stop?
I have XHTML (1.0 transitional) that looks as follows
- <div id="projectContent">
- <div class="project" id="dataLorem">
- <div class="projectImage">
- <img src="images/lorem-ipsum.png" alt="image alt text" />
- </div>
- <div class="projectDataHead">
- <div class="projectClient">
- <div class="dataName">Client</div>
- <div class="dataValue">Lorem Ipsum</div>
- </div>
- <div class="projectTitle">
- <div class="dataName">Project</div>
- <div class="dataValue">Development & content management</div>
- </div>
- <div class="projectSkillsRelated">
- <div class="dataName">Primary Skills</div>
- <div class="dataValue">HTML, CSS, Javascript</div>
- </div>
- <div class="projectSkillsSupport">
- <div class="dataName">Supporting Skills</div>
- <div class="dataValue">Microsoft Office</div>
- </div>
- </div>
- <div class="projectDescription">
- <div class="dataName">Description</div>
- <div class="dataValue">
- <p>Description goes here.</p>
- <p>
- Examples of work:
- </p>
- <ul>
- <li><a href="#">Link</a></li>
- <li><a href="#">Link</a></li>
- <li><a href="#">Link</a></li>
- </ul>
- </div>
- </div>
- </div>
- <div class="project" id="dataFoobar">
- ...
- </div>
- <!--etc-->
- </div>
I want to create a menu based on each project in projectContent, but I don't want the menu to appear at all for agents that don't have javascript. Simple enough right? Create new nodes and insert them into the DOM.
Here is the code I expect the nodes to represent once inserted into the DOM. In this particular case It will be inserted as a sibling before projectContent.
- <div class="projectNav">
- <ul>
- <li>
- <a href="#dataLorem">
- <span class="one">Lorem Ipsum</span>
- <span class="two">Development & content management</span>
- </a>
- </li>
- <li>
- <a href="#dataFoobar">
- <span class="one">Foobar</span>
- <span class="two">Web application prototype</span>
- </a>
- </li>
- <li>
- <a href="#dataClientC">
- <span class="one">Client Name C</span>
- <span class="two">Hello World!</span>
- </a>
- </li>
- <!--etc-->
- </ul>
- </div>
- <div id="projectContent">
- ...
- </div>
I have attempted this with jQuery 1.4.2 and raw (well, almost raw) javascript, but something strange happens.
First I mocked up the styles and everything using flat html. Great, wonderful, everything works fine. When I create the elementNodes with javascript some wierd stuff started happening.
Using my keyboard I tab through the page until I reach the menu inserted into the DOM. I can tab into the first link generated then I become stuck. It will not allow me to tab forward nor backwards (shift + tab). Since accessibility is important to me, this is a problem. I am also concerned that I may be missing something really obvious. I even tried manually setting tabindex="0" with no effect. onClick and onKeyDown events fire, the href has no trouble and the styles look just fine, so it is just the tabbing that is really leaving me scratching my head.
I tried viewing generated source (web master toolbar for Firefox), then copying the code created into flat xhtml, and it works fine!
I am using the file hosted by google.
- <script language="javascript" type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
Here is the jQuery I attempted using:
- function initPage()
- {
- var aProjects = $('#projectContent .projectData');
-
- var sOut = '<div class="projectNav">';
- sOut += '<ul>';
- for(var x = 0; x < aProjects.length; x++)
- {
- oElem = $(aProjects[x]);
- sOut += '<li>';
- sOut += '<a href="#'+oElem.attr('id')+'" onclick="toggleProject(this); return false;" onkeydown="if(keyIsEnter(event)){toggleProject(this);}; return false;" taborder="0">';
- sOut += '<span class="one">'+oElem.find('.projectClient .dataValue').html()+'</span>';
- sOut += '<span class="two">'+oElem.find('.projectTitle .dataValue').html()+'</span>';
- sOut += '</a>';
- sOut += '</li>';
- };
- sOut += '</ul>';
- sOut += '</div>';
-
- $("#projectContent").before(sOut);
- }
- $(initPage);
- function toggleProject(argAnchor)
- {
- $('.projectNav .active').removeClass('active');
- $(argAnchor.parentNode).addClass('active');
- }
- function keyIsEnter(e)
- {
- var evt = (e) ? e : window.event; //IE reports window.event
- return (evt.keyCode==13)?true:false;
- }
Which had the same results as almost no jQuery:
- function initPage()
- {
- var aProjects = $('#projectContent .projectData');
-
- var oContainer = document.createElement('div');
- oContainer.setAttribute('class','projectNav');
- var oList = document.createElement('ul');
-
- for(var x = 0; x < aProjects.length; x++)
- {
- oElem = $(aProjects[x]);
-
- oListItem = document.createElement('li');
-
- oAnchor = document.createElement('a');
- oAnchor.href = '#'+oElem.attr('id');
- oAnchor.onclick = function(e){toggleProject(this); return false;};
- oAnchor.onkeydown = function(e){if(keyIsEnter(e)){toggleProject(this);}; return false;};
-
- oSpan1 = document.createElement('span');
- oSpan1.setAttribute('class','one');
- sSpan1Text = document.createTextNode(oElem.find('.projectClient .dataValue').html());
- oSpan1.appendChild(sSpan1Text);
-
- oSpan2 = document.createElement('span');
- oSpan2.setAttribute('class','two');
- sSpan2Text = document.createTextNode(oElem.find('.projectTitle .dataValue').html());
- oSpan2.appendChild(sSpan2Text);
-
- oAnchor.appendChild(oSpan1);
- oAnchor.appendChild(oSpan2);
- oListItem.appendChild(oAnchor);
- oList.appendChild(oListItem);
-
- };
- oContainer.appendChild(oList);
-
- // Add Navigation to document
- (document.getElementById('projectContent').parentNode).insertBefore(oContainer, document.getElementById('projectContent'));
- }
- $(initPage);
- function toggleProject(argAnchor)
- {
- $('.projectNav .active').removeClass('active');
- $(argAnchor.parentNode).addClass('active');
- }
- function keyIsEnter(e)
- {
- var evt = (e) ? e : window.event; //IE reports window.event
- return (evt.keyCode==13)?true:false;
- }
What is happening? How can I make it work using compliant code?