递归函数返回不退出函数

时间:2018-02-19 10:59:55

标签: javascript recursion

我很难修复递归函数,这是一个简化的工具,用于扫描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;
}

因此该函数应该扫描给定DOM元素的子元素(可能还有它们的子元素)并返回找到的元素,否则再深入并重试。

这是可调试的DEMO

正如您在日志中看到的那样,它触发了console.log('found');但它没有让函数返回它,但继续并最终返回false(截至未找到)。如何解决?

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>

3 个答案:

答案 0 :(得分:2)

return退出您找到该元素的find调用,但不会解除导致该元素的所有调用。

而不是

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

...在else中,您需要查看是否有元素,如果有,请返回:

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

这让它传播到链条上。

更新了实例:

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>

这是关于递归函数的关键之一:当它们自称时,它们必须查看该调用的结果并在适当时传播它。

答案 1 :(得分:1)

未对find的递归调用结果进行处理。你应该检查递归调用的结果值,并在递归调用找到元素时返回它的值:

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

答案 2 :(得分:0)

查询选择器潜在浪费

您的tools函数库是一项很好的工作,但它表明对querySelector实际工作方式缺乏了解。为了证明我的观点,你的整个程序将在下面重写

// 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>

&#13;
&#13;
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>
&#13;
<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>
&#13;
&#13;
&#13;

多个班级

上面,querySelector已经完成了我们需要的所有内容,但是你的tools.has_class表现出另一个缺陷 - 元素可以包含多个类。您的功能将跳过具有属性class="test foo"的孩子。

为了便于讨论,如果你必须自己实现这个,你可以调整你的has_class函数来按空格分隔元素的类,然后检查每个类的匹配 - 或者您可以使用已包含contains函数

Element.classList

&#13;
&#13;
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
&#13;
<div class="foo test bar"></div>
&#13;
&#13;
&#13;