Danielo515
Danielo515

Reputation: 7061

javascript search node by title in collection and return it

I wrote a function that tooks an HTMLcollection and a text and iterates that collection searching for a node with that tittle. Then it should return that node. The problem is that it is returning allways undefined, even when it finds the correct node: In the code you can see there is a console.log that outputs if the current node has that title. On the console I can see one "true" which is what I expect, but the loop does not stop there and the function does not return anything.

function searchButtonbyText(group,text){
    for(var i=0;i<group.length;i++){
        for(var j=0; j< group[i].children.length; j++){
            var child = group[i].children[j];
            console.log(child.title == text );
            if( child.title == text ){
                console.log(child);
                return child;
            }
        }
    }
}

I tried with simpler functions that just returns certain node with the collection as input and they work correctly. For example

function tt(coll){ return coll[7].children[0]}

Thanks in advance.

EDIT

I edited the code to correct the error. Now the code is working but the approach on the selected answer is much better.

Upvotes: 1

Views: 245

Answers (1)

Jason Sperske
Jason Sperske

Reputation: 30416

Here is a recursive approach (demoenter link description here)

function searchButtonByText(element, text) {
    var match;
    if(element['title'] && element['title'] === text) {
        return element;
    } else {
        for(var i = 0; i < element.children.length; i++) {
            match = searchButtonByText(element.children[i], text);
            if(match) {
                break;
            }
        }
        return match;
    }
}

With the following HTML:

<ul id='Group'>
    <li><button title='One'>One</button></li>
    <li><button title='Two'>Two</button></li>
    <li>
        <ul>
            <li><button title='A'>A</button></li>
            <li><button title='B'>B</button></li>
            <li><button title='C'>C</button></li>
        </ul>
    </li>
    <li><button title='Three'>Three</button></li>
</ul>

Then you can invoke it like this:

console.log(searchButtonByText(document.getElementById('Group'), 'Two'));

It starts at any arbitrary node, and then looks at each child until it finds a match (no matter how nested they are). Or you can generalize this even further and make the test a callback like this (demo):

function searchByFunction(element, matcher) {
    var match;
    if (matcher(element)) {
        return element;
    } else {
        for (var i = 0; i < element.children.length; i++) {
            match = searchByFunction(element.children[i], matcher);
            if (match) {
                break;
            }
        }
        return match;
    }
}

Then you just need to pass a function that returns a true or false value based on any logic you an devise like this:

console.log(searchByFunction(document.getElementById('Group'), function(element) {
    return element['title'] && element['title'] === 'C';
}));

Finally to address the requirement of dealing with an array of elements (which each might have children) here is a workable solution (demo):

function searchByFunction(element, matcher) {
    var match,
        i;
    if(element.length) {
        for (i = 0; i < element.length; i++) {
            match = searchByFunction(element[i], matcher);
            if (match) {
                break;
            }
        }
    } else {
        if (matcher(element)) {
            return element;
        } else {
            if(element.children) {
                for (i = 0; i < element.children.length; i++) {
                    match = searchByFunction(element.children[i], matcher);
                    if (match) {
                        break;
                    }
                }
            }
        }
    }
    return match;
};

Along with a helper function to generate matching functions:

function buildTitleMatcher(title) {
    return function(element) {
        return element['title'] && element['title'] === title;
    };
};

You can use it with a single element like this:

console.log(searchByFunction(document.getElementById('Group'), buildTitleMatcher('C')));

Or with an array of elements like this:

console.log(searchByFunction(document.getElementsByTagName('li'), buildTitleMatcher('B')));

Upvotes: 2

Related Questions