3

I have a function that gets an array of DOM elements (based on tags) within a div.

Pseudocode:
1. Say I wanted to get all input and textarea elements within a table myTbl
2. Declare resultingArray = null
3. For each tag (ex: input, textarea)
4.   tagArray = Get all elements based on tag
5.   Create another array by manually looping through tagArray and adding 
it to resultingArray (the return type is dynamic collection and not an array.

Functionally, it works but it takes too long. Is there a way to do what I am trying to do faster?

1
  • 1
    You can use concat if you slice the collection first. This is a very fast way to convert a dynamic node list into an array. I've posted some code below, and there is good information here: shifteleven.com/articles/2007/06/28/… Commented Sep 12, 2009 at 7:35

6 Answers 6

4

Based on Ben's elements idea, here's another shot with a non nested loop.

var tagNames = { 'SELECT' : true, 'INPUT' : true, 'TEXTAREA' : true }; //use an object for faster lookups
var rawElemsArray = document.getElementById("form-name").elements;
var elems = [];
for (var i = rawElemsArray.length - 1; i >= 0; i--) {
    if (tagNames[rawElemsArray[i].tagName]) {
        elems.push(rawElemsArray[i]);
    }
}

EDIT: form.elements is defined in level 0 DOM, so I bet it is cross-browser. You can also use childNodes (which is cross-browser too) if that serves your purpose. The difference between them is that childNodes selects all nodes (div, p, span etc) and also empty text nodes (in non-IE browsers) while elements returns only the form controls.

Sign up to request clarification or add additional context in comments.

3 Comments

Very elegant. This should be considerably faster than looping over getElementsByTagName.
This is great, but it does only work for form elements, and specifically form elements that are all in the same form. I know that has been mentioned in Ben's post, but it seems important enough to mention again.
Is this solution cross browser compatible?
2

This definitely the type of problem that begs for a jQuery solution

var $elements = jQuery( '#id-of-table input, #id-of-table textarea' );
$elements.each( function( i, element )
{
   // whatever you need here
} );

Comments

1

If your inputs and textareas are all within the same <form>, take a look at the form.elements DOM property. That way your code could be simplified to:

var resultingArray = document.getElementById("form-name").elements;

Edit:

If your list of tag names is dynamic and you can't use a library, I don't think you'll be able to easily get away from the looping approach, but you can try to make it as light-weight as possible:

var result = [], nTags = tags.length, elements, nElements;

for (var i = 0; i < nTags; i++) {
    elements = table.getElementsByTagName(tag);
    nElements = elements.length;

    for (var j = 0; j < nElements; j++) {
        result.push(element);
    }
}

You could perhaps look into XPath expressions as well, but beware browser differences (for example, the code below won't work in IE, though there is an alternative for it).

var result = document.evaluate(tags.join("|"), table, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

5 Comments

I dont want to get all the elements. Only a selected few (input, span etc - this list is dynamically constructed)
I tried your solution. It works functionally. But it's actually taking 30% more than my previous solution :(
Good heavens. I can't imagine why; that should be about as straightforward as it gets! ;-)
for...in isn't the right construct for looping over an Array or NodeList in JS, you need the for(i= 0; i<n; i++) form. For an Array, the values of tag will be 0, 1, 2 and so on; for the NodeList you would get method names. The XPath, on the other hand, should work fast where available.
@bobince — Right you are! Clearly, I was conflating for...in with foreach...in, which isn't even cross-browser. I've corrected my example.
1

You can call or apply Array.slice to an HTMLCollection to convert it to a string. That allows you to use concat, which I think is the fastest possible solution:

function getElementsByTagNames(context, tags) {
    var res = [], 
        i = tags.length,
        slice = Array.prototype.slice;

    // Convert HTMLCollections to arrays and push onto the res array
    while(i--) res.push(slice.call(context.getElementsByTagName(tags[i])));

    // Use one concat call to merge all the arrays
    return Array.prototype.concat.apply([], res);
}

getElementsByTagNames(document.body, ['input', 'textarea']);

Keep in mind that this does not return the nodes in document order. It will return all the <input>s grouped together and all of the <textarea>s grouped together.

Comments

0

I'm not 100% sure I understand what you're trying to do, but this might point you in the right direction (note that it uses jQuery):

<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
    function getStuff(){
        resultArr = [];
        var inputs = $('#myTbl').find('input');
        var tareas = $('#myTbl').find('textarea'); 
        // do the looping bit which I don't completely understand
    }
</script>

2 Comments

well the problem is I can't combine two arrays without looping through the elements manually. Maybe this will help : codingforums.com/showthread.php?t=47683
I cannot use JQuery. I am looking for a Javascript solution
-1

i had similar situation with a table of 5 columns and up to 1200 rows (not all rows had all columns filled)

So all i did was put onclick handlers on onmouseover event for a table cell (putting onclick hanlders immediately on td would take too long too). And onclick handler would create textarea with text found in table cell.

Then i just got away with jQuery selectors!

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.