简体   繁体   English

在递归函数中返回不会退出函数

[英]Return in recursive function doesn't exit function

I am having hard time fixing my recursive function, which is a simplified tool for to scan DOM items and return matching element when found somewhere within document. 我很难修复我的递归函数,这是一个简化的工具,用于扫描DOM项并在文档中的某个位置找到时返回匹配元素。

find: function(selector, element) {
    if(selector !== undefined) {
        if(element === undefined) {
            element = this.e;
        }
        var elements = element.childNodes;
        for(var i = 0; i < elements.length; i++) {
            if(elements[i].nodeType === 1) {
                console.log(elements[i]);
                if(this.has_class(selector, elements[i]) === true) {
                    console.log('YAY, found it', elements[i]);
                    return elements[i];
                } else {
                    if(elements[i].childNodes.length > 0) {
                        this.find(selector, elements[i]);
                    }
                }
            }
        }
    }
    return false;
}

So the function should scan through children (and possibly their children) of given DOM element and return found element, otherwise go deeper and try again. 因此,该函数应扫描给定DOM元素的子元素(可能是它们的子元素),并返回找到的元素,否则,请更深入尝试。

This is debuggable DEMO . 这是可调试的DEMO

As you can see in logs, it triggered console.log('found'); 如您在日志中所见,它触发了console.log('found'); but it did not leave function returning it, but continue and eventually returned false (as of not found). 但是它并没有让函数返回它,而是继续并最终返回了false(截至未找到)。 How can it be fixed? 如何解决?

 var tools = { e: document.querySelector('.breadcrumbs'), has_class: function(name, element) { if (element.className === name) { return true; } return false; }, find: function(selector, element) { if (selector !== undefined) { if (element === undefined) { element = this.e; } var elements = element.childNodes; for (var i = 0; i < elements.length; i++) { if (elements[i].nodeType === 1) { console.log(elements[i]); if (this.has_class(selector, elements[i]) === true) { console.log('YAY, found it', elements[i]); return elements[i]; } else { if (elements[i].childNodes.length > 0) { this.find(selector, elements[i]); } } } } } return false; } }; console.log(tools.find('test')); 
 <div class="breadcrumbs" data-active="true"> <div class="bc_navigation" onclick="events.bc_toggle(event, this);"> <span class="bc_arrow"></span> </div> <div class="bc_content"> <div class="bc_wrapper"> <div class="step"> <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a> </div> <div class="step"> <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a> </div> <div class="step"> <span class="dot"></span><a>Russia - Saudi Arabia</a> </div> </div> </div> </div> 

The return exits the call to find where you found the element, but doesn't unwind all the calls that lead up to it. return退出调用以find您在哪里找到了元素,但不会展开导致该元素的所有调用。

Instead of 代替

this.find(selector, elements[i]);

...in your else , you need to see if you got the element and, if so, return: ...在您的else ,您需要查看是否有该元素,如果是,则返回:

var result = this.find(selector, elements[i]);
if (result) {
    return result;
}

That lets it propagate up the chain. 这使其可以向上传播。

Updated Live Example: 更新了实时示例:

 var tools = { e: document.querySelector('.breadcrumbs'), has_class: function(name, element) { if (element.className === name) { return true; } return false; }, find: function(selector, element) { if (selector !== undefined) { if (element === undefined) { element = this.e; } var elements = element.childNodes; for (var i = 0; i < elements.length; i++) { if (elements[i].nodeType === 1) { console.log(elements[i]); if (this.has_class(selector, elements[i]) === true) { console.log('YAY, found it', elements[i]); return elements[i]; } else { if (elements[i].childNodes.length > 0) { var result = this.find(selector, elements[i]); if (result) { return result; } } } } } } return false; } }; console.log(tools.find('test')); 
 <div class="breadcrumbs" data-active="true"> <div class="bc_navigation" onclick="events.bc_toggle(event, this);"> <span class="bc_arrow"></span> </div> <div class="bc_content"> <div class="bc_wrapper"> <div class="step"> <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a> </div> <div class="step"> <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a> </div> <div class="step"> <span class="dot"></span><a>Russia - Saudi Arabia</a> </div> </div> </div> </div> 

This is one of the key things about recursive functions: When they call themselves, they must look at the result of that call and propagate it when appropriate. 这是递归函数的关键内容之一:当他们调用自己时,他们必须查看该调用的结果并在适当的时候传播它。

Result of the recursive call to find isn't processed. 递归调用find结果未处理。 You should check to result value of the recursive call and should return its value when the recursive call located the element: 您应该检查递归调用的结果值,并在递归调用找到元素时返回其值:

find: function(selector, element) {
    if(selector !== undefined) {
        if(element === undefined) {
            element = this.e;
        }
        var elements = element.childNodes;
        for(var i = 0; i < elements.length; i++) {
            if(elements[i].nodeType === 1) {
                console.log(elements[i]);
                if(this.has_class(selector, elements[i]) === true) {
                    console.log('YAY, found it', elements[i]);
                    return elements[i];
                } else {
                    var foundElement = this.find(selector, elements[i]);
                    // *** Added this check to return the located element from the recursive call
                    if (foundElement != false) {
                        return foundElement;
                    }
                }
            }
        }
    }
    return false;
}

querySelector potential wasted querySelector潜在的浪费

Your tools library of functions is a good effort, but it shows a lack of understanding of how querySelector actually works. 您的tools功能库是一项不错的工作,但是它表明对querySelector实际工作方式缺乏了解。 To demonstrate my point, your entire program is rewritten below 为了说明我的观点,您的整个程序在下面进行了重写

// starting with the Document element, get the first child matching '.breadcrumbs'
const elem =
  document.querySelector ('.breadcrumbs')

// starting with `elem`, get the first child matching '.test'
const child =
  elem.querySelector ('.test')

console.log (child)
// <a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a>

 const elem = document.querySelector ('.breadcrumbs') const someChild = elem.querySelector ('.test') console.log (someChild) // <a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a> 
 <div class="breadcrumbs" data-active="true"> <div class="bc_navigation" onclick="events.bc_toggle(event, this);"> <span class="bc_arrow"></span> </div> <div class="bc_content"> <div class="bc_wrapper"> <div class="step"> <span class="dot"></span><a onclick="events.go_home(event, this);">Landing</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-sport][1|0|0|0|0]">Soccer</a> </div> <div class="step"> <span class="dot"></span><a href="#prematch[prematch-group][1|4|0|0|0]">International</a> </div> <div class="step"> <span class="dot"></span><a class="test" href="#prematch[prematch-event][1|4|16|10|0]">Int - World Cup</a> </div> <div class="step"> <span class="dot"></span><a>Russia - Saudi Arabia</a> </div> </div> </div> </div> 

multiple classes 多班

Above, querySelector already does everything we need it to, but your tools.has_class exhibits another deficiency – elements can have more than one class. 上面, querySelector已经完成了我们需要做的所有事情,但是您的tools.has_class表现出另一个缺陷–元素可以具有多个类。 Your function would've skipped over a child that had an attribute class="test foo" . 您的函数将跳过具有class="test foo"属性的孩子。

For the sake of discussion, if you had to implement this on your own, you could adapt your has_class function to separate the element's classes by space, then check each class for a match – or you could use Element.classList which already includes a contains function 为了便于讨论,如果必须自己实现,则可以调整has_class函数以按空格分隔元素的类,然后检查每个类是否匹配–或可以使用Element.classList其中已经contains功能

 const elem = document.querySelector ('.test') console.log (elem.classList) // { DOMTokenList [ "foo", "test", "bar" ] } console.log (elem.classList.contains ('foo')) // true console.log (elem.classList.contains ('test')) // true console.log (elem.classList.contains ('dog')) // false 
 <div class="foo test bar"></div> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM