代码之家  ›  专栏  ›  技术社区  ›  Mevia

递归函数中的Return不退出函数

  •  1
  • Mevia  · 技术社区  · 7 年前

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

    正如您在日志中看到的,它触发了控制台。日志(“找到”);但它并没有让函数返回它,而是继续并最终返回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 回复  |  直到 7 年前
        1
  •  3
  •   T.J. Crowder    7 年前

    这个 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>

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

        2
  •  1
  •   R. Oosterholt Alex Vayda    7 年前

    递归调用的结果 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;
    }
    
        3
  •  0
  •   Mulan    7 年前

    querySelector潜在浪费

    你的 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>
    

    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>

    多个类

    在上面 查询选择器 已经做了我们需要的一切 tools.has_class 另一个缺陷元素可以有多个类别。您的函数将跳过具有属性的子级 class="test foo" .

    为了便于讨论,如果您必须自己实现这一点,您可以调整 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>