简体   繁体   中英

How to optimize the code like “this.parentNode.parentNode.parentNode…”?

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

As you see, there is a part of my code like this:

this.parentNode.parentNode.parentNode.parentNode;  

Is there another way to optimize the code (without using jQuery)?

You can use a non recursive helper function, for example:

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

You can use recursive helper function, for example:

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

var child = getNthParent(someElement, 4);

An alternative approach


Your goal

According to your comments on the original question, your overall goal is this:

There is a list of blogs.Each blog has a button like "edit" and "delete". When I click such buttons I want to find it's blog element.

I believe the best approach to solve the problem you're facing (as opposed to answering the question you asked - sorry!) is to approach the problem in a different manner.

From your comments, you said you have something like this:

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

And your goal is to add click event handlers to the button elements for each blog-link item.

So let's just translate that goal from plain English into pseudo-code:

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

Example script

Working example at: 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>

Explanation

The way you've chosen to implement a solution is valid, but I thought I'd give you a different way to look at the same problem.

In my opinion, the inner tags of the blog entity should not have to have knowledge of the structure or properties of the surrounding HTML for your edit and delete buttons to work.

Your original solution has to work backwards from each button up the chain of parents until it finds what it assumes is the correct parent element, based on a brittle method like hard-coding moving up N times in the chain of parent elements. Wouldn't it be nicer if we could use normal JavaScript element selection to be absolutely sure of what we're selecting? That way, no matter how the HTML structure might change, our JavaScript isn't going to break as long as the classes and IDs remain consistent.

This solution iterates over every blog-item in the #blog-list element:

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

Within the forEach loop, we grab arrays containing .delete-button and .edit-button elements. On each of those elements, we add the appropriate event handler to the onclick property:

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

For each deleteButton element in the array, we assign an anonymous function to the event handler onclick . Creating this anonymous function allows us to create a closure.

This means each deleteButton.onclick function will individually "remember" which blogitem it belongs to.

Check out this SO question/answer about closures for more info: How do JavaScript closures work?

And we throw in the HTMLCollection.prototype.forEach... line to provide forEach functionality to all HTMLCollection objects. Functions like getElementsByClassName return an HTMLCollection object. It acts exactly like an array for the most part, but it doesn't let us use forEach by default. A note about compatibility: you can certainly use a standard for loop, since forEach isn't available in all browsers (mostly old IE browsers are to blame). I prefer the forEach idiom.

End result

The end result is a little bit longer code, since the scope of the problem is a little wider than the question you actually asked. The advantage is that this code is much more flexible and resistant to being broken by changes to the HTML structure.

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

Surprise! I print the event through console.log. And find a array like this inside the event: 在此输入图像描述

And I find the element that I want :

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

and it work! How magic! Maybe event agent is another way!
Thank you all the same for giving answers!
Next time before asking questions I'll think over first! :D

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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