Nick
Nick

Reputation: 7525

Can this function be made faster? Javascript

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?

Upvotes: 3

Views: 263

Answers (6)

Chetan S
Chetan S

Reputation: 23823

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.

Upvotes: 4

Prestaul
Prestaul

Reputation: 85224

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.

Upvotes: 1

Ben Blank
Ben Blank

Reputation: 56634

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);

Upvotes: 1

Peter Bailey
Peter Bailey

Reputation: 105914

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
} );

Upvotes: 2

brettkelly
brettkelly

Reputation: 28245

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>

Upvotes: 0

Eimantas
Eimantas

Reputation: 49354

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!

Upvotes: -1

Related Questions