【问题标题】:How to optimize the code like "this.parentNode.parentNode.parentNode..."?如何优化“this.parentNode.parentNode.parentNode ...”之类的代码?
【发布时间】:2016-02-01 11:43:48
【问题描述】:
var topClick = function() {
  child = this.parentNode.parentNode.parentNode.parentNode;
  child.parentNode.removeChild(child);
  var theFirstChild = document.querySelector(".m-ctt .slt");
  data[child.getAttribute("data-id")].rank = 5;
  insertBlogs();
};

如你所见,我的代码中有一部分是这样的:

this.parentNode.parentNode.parentNode.parentNode;  

还有其他优化代码的方法吗(不使用 jQuery)?

【问题讨论】:

  • 你试过循环吗?
  • @Bergi — 循环的效率会降低。 OP 知道他们想在树上走多远。该对象似乎是“少打字”而不是“快速搜索匹配规则的元素”。
  • @kpsingh — 搜索方向错误。问题在于向上树,而不是向下。
  • 在支持的浏览器中,有一个native .closest() method(纯JavaScript),其工作方式类似于jQuery的.closest()方法。
  • 您可能还想考虑命名变量的方式。

标签: javascript


【解决方案1】:

您可以使用非递归辅助函数,例如:

function nthParent(element, n) {
  while(n-- && element)  
    element = element.parentNode;
  return element;
}

【讨论】:

    【解决方案2】:

    可以使用递归辅助函数,例如:

    function getNthParent(elem, n) {
        return n === 0 ? elem : getNthParent(elem.parentNode, n - 1);
    }
    
    var child = getNthParent(someElement, 4);
    

    【讨论】:

    • 谢谢!最后我接受你的方法。但是当使用一个用函数表达式初始化的变量来定义它时它说Uncaught ReferenceError: getNthParent is not defined所以我改变了一点return n === 0 ? elem : arguments.callee(elem.parentNode, n - 1);
    • @yaochiqkl arguments.callee 在我看来有点麻烦。我会改用命名函数表达式。 var myFn = function getNthParent(elem, n) { ... }
    【解决方案3】:

    另一种方法


    你的目标

    根据您对原始问题的 cmets,您的总体目标是:

    有一个博客列表。每个博客都有一个按钮,如“编辑”和“删除”。当我点击这些按钮时,我想找到它的博客元素。

    我相信解决您面临的问题的最佳方法(而不是回答您提出的问题 - 抱歉!)是以不同的方式解决问题。

    从你的 cmets 中,你说你有这样的东西:

    <ul id="blog-list">
      <li class="blog-item">
        <a href="blog1.com">
          Boats galore!
        </a>
        <span class="blog-description">
          This is a blog about some of the best boats from Instagram.
        </span>
        <span class="blog-button delete-button">
          Delete
        </span>
        <span class="blog-button edit-button">
          Edit
        </span>
      </li>
      <li class="blog-item">
        <a href="blog2.com">
          Goats galore!
        </a>
        <span class="blog-description">
          A blog about the everyday adventures of goats in South Africa.
        </span>
        <span class="blog-button delete-button">
          Delete
        </span>
        <span class="blog-button edit-button">
          Edit
        </span>
      </li>
      <li class="blog-item">
        <a class="blog-link" href="blog3.com">
          Totes galore!
        </a>
        <span class="blog-description">
          A blog about containers and bags, and the owners who love them.
        </span>
        <span class="blog-button delete-button">
          Delete
        </span>
        <span class="blog-button edit-button">
          Edit
        </span>
      </li>
    </ul>
    

    您的目标是将click 事件处理程序添加到每个blog-link 项目的button 元素。

    所以让我们把这个目标从简单的英语翻译成伪代码:

    for each `blog-link` `b`
      delete_button.onclick = delete_handler(`b`);
      edit_button.onclick = edit_handler(`b`);
    

    示例脚本

    工作示例:http://jsfiddle.net/vbt6bjwy/10/

    <script>
      function deleteClickHandler(event, parent) {
        event.stopPropagation();
        parent.remove();
      }
    
      function editClickHandler(event, parent) {
        event.stopPropagation();
        var description = parent.getElementsByClassName("blog-description")[0];
        description.innerHTML = prompt("Enter a new description:");
      }
    
      function addClickHandlers() {
        var bloglistElement = document.getElementById("blog-list");
        var blogitems = bloglistElement.getElementsByClassName("blog-item");
    
        blogitems.forEach(function(blogitem){
          var deleteButtons = blogitem.getElementsByClassName("delete-button");
    
          deleteButtons.forEach(function(deleteButton){
            deleteButton.onclick = function(event) {
              return deleteClickHandler(event, blogitem);
            }
          });
    
          var editButtons = blogitem.getElementsByClassName("edit-button");
    
          editButtons.forEach(function(editButton){
            editButton.onclick = function(event) {
              return editClickHandler(event, blogitem);
            }
          });
        });
      }
    
      HTMLCollection.prototype.forEach = Array.prototype.forEach;
      addClickHandlers();
    </script>
    

    说明

    您选择实施解决方案的方式是有效的,但我想我会给您一种不同的方式来看待同一个问题。

    在我看来,blog 实体的内部标签不必了解周围 HTML 的结构或属性,您的 editdelete 按钮也能正常工作。

    您的原始解决方案必须从父链中的每个 button 逆向工作,直到找到它假定的正确父元素,这基于一种脆弱的方法,例如硬编码在链中向上移动 N 次的父元素。如果我们可以使用普通的 JavaScript 元素选择来绝对确定我们选择的是什么,那不是更好吗?这样一来,无论 HTML 结构如何变化,只要类和 ID 保持一致,我们的 JavaScript 就不会中断。

    此解决方案遍历 #blog-list 元素中的每个 blog-item

    blogitems.forEach(function(blogitem){ ... });

    forEach 循环中,我们抓取包含.delete-button.edit-button 元素的数组。在每个元素上,我们将适当的事件处理程序添加到 onclick 属性:

    deleteButtons.forEach(function(deleteButton){
      deleteButton.onclick = function(event) {
        return deleteClickHandler(event, blogitem);
      }
    });
    

    对于数组中的每个 deleteButton 元素,我们将一个匿名函数分配给事件处理程序onclick。创建这个匿名函数允许我们创建一个闭包。

    这意味着每个deleteButton.onclick 函数将单独“记住”它属于哪个blogitem

    查看这个关于关闭的 SO 问题/答案以获取更多信息:How do JavaScript closures work?

    我们加入HTMLCollection.prototype.forEach... 行,为所有HTMLCollection 对象提供forEach 功能。像getElementsByClassName 这样的函数返回一个HTMLCollection 对象。它在很大程度上与数组完全一样,但默认情况下它不允许我们使用forEach。关于兼容性的说明:您当然可以使用标准的 for 循环,因为 forEach 并非在所有浏览器中都可用(主要是旧的 IE 浏览器是罪魁祸首)。我更喜欢forEach 成语。

    最终结果

    最终结果是代码有点长,因为问题的范围比您实际提出的问题要宽一些。优点是这段代码更加灵活,并且不会被 HTML 结构的更改破坏。

    【讨论】:

    • 我很感动,你热情地教我。我真的从中学到了很多,知道如何以更好的方式解决它,并对闭包有了更深入的了解。我会反复阅读它以获得这种方法。非常感谢!
    • @yaochiqkl 很高兴您能从我的回答中受益。如果您对 JavaScript 感兴趣并阅读更多关于闭包之类的内容,我建议您阅读 Crockford 的 Javascript: the Good Parts。特别是,第 4 章涵盖了函数、JS 中的作用域以及 JS 中的闭包。整本书很短,可以快速通读,但信息量很大。它发表于 2008 年,但它仍然与现代 JS 非常相关。
    【解决方案4】:
    var topClick = function(event){
        console.log(event);
        child = this.parentNode.parentNode.parentNode.parentNode;
        child.parentNode.removeChild(child);
        var theFirstChild = document.querySelector(".m-ctt .slt");
        data[child.getAttribute("data-id")].rank = 5;
        insertBlogs();
    };
    

    惊喜!我通过 console.log 打印事件。 并在事件中找到这样的数组:

    我找到了我想要的元素:

    function(event){
    console.log(event.path[4]);
    child = event.path[4];
    }
    

    它工作!多么神奇!也许事件代理是另一种方式!
    谢谢大家的回答!
    下次提问之前我会先考虑清楚的! :D

    【讨论】:

    • 这不是标准,可能并非所有浏览器都支持。见stackoverflow.com/a/30021867/2011448
    • 这假定了问题中未提供的信息。 (我知道这是你的问题,但仍然......)
    猜你喜欢
    • 2022-11-23
    • 1970-01-01
    • 2012-12-12
    • 2015-09-19
    • 2011-04-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多