最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - What is the most elegant, reliable and efficient way to get the grandparent's first child in JQuery? - Stac

programmeradmin2浏览0评论

I have a piece of HTML like

<form action="/AssetBundle/DownloadFile" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#masterLinkHolder" id="form0" method="post"></form>
  <table>
      <tr>
          <td>someimage.png</td>
          <td><img src="imageicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
      <tr>
          <td>somedocument.docx</td>
          <td><img src="docicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
  </table>
  <input type="hidden" id="file2Download" />
</form>

and when a button is clicked I want to set the value of the input with id file2Download to be the file name (e.g. someimage.png, somedocument.docx) from the same tr as the button, and then submit the form. So I need help filling out

<script type="text/javascript">
    $('button').click(function () {
        $('#file2Download').val(
           // ... ?
        );
        $('#id0').submit();
    });
</script>

in the proper way. I know that I'm looking at a tree like

               tr
           /   |    \ 
          td   td    td
               |      \
              img     button

and trying to go from the button to the grandparent tr and then to the first child of that tr.

I have a piece of HTML like

<form action="/AssetBundle/DownloadFile" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#masterLinkHolder" id="form0" method="post"></form>
  <table>
      <tr>
          <td>someimage.png</td>
          <td><img src="imageicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
      <tr>
          <td>somedocument.docx</td>
          <td><img src="docicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
  </table>
  <input type="hidden" id="file2Download" />
</form>

and when a button is clicked I want to set the value of the input with id file2Download to be the file name (e.g. someimage.png, somedocument.docx) from the same tr as the button, and then submit the form. So I need help filling out

<script type="text/javascript">
    $('button').click(function () {
        $('#file2Download').val(
           // ... ?
        );
        $('#id0').submit();
    });
</script>

in the proper way. I know that I'm looking at a tree like

               tr
           /   |    \ 
          td   td    td
               |      \
              img     button

and trying to go from the button to the grandparent tr and then to the first child of that tr.

Share Improve this question edited Jul 12, 2021 at 11:26 Brian Tompsett - 汤莱恩 5,89372 gold badges61 silver badges133 bronze badges asked May 19, 2015 at 20:42 user4905335user4905335 3713 silver badges14 bronze badges 0
Add a ment  | 

5 Answers 5

Reset to default 10

tl;dr

The accepted answer is the second slowest suggestion.

The difference between vanilla javascript and jQuery is huge.

There are many ways to skin a cat, or find a grandparents first child.

Use vanilla javascript whenever possible.

Results

Here are the jsPerf results of all of the methods suggested thus far. Remember that higher numbers for ops/sec are better, I've ordered them from quickest to slowest

  1. Element.parentNode.parentNode.firstElementChild.textContent;

    @canon
    (Demo)
    characters = 60
    ops/sec = 3,239,703

  2. Element.parentNode.parentNode.children[0].textContent;

    @canon
    (Demo)
    characters = 54
    ops/sec = 1,647,235

  3. Element.parentNode.parentNode.cells[0].textContent;

    @canon
    (Demo)
    characters = 51
    ops/sec = 1,558,070

  4. Element.parentNode.parentNode.querySelector('td:first-child').textContent;

    @Tiny Giant
    (Demo)
    characters = 74
    ops/sec = 1,189,826

  5. document.getElementById(Element.dataset.select).textContent;

    @guest271314
    (Demo)
    characters = 60
    ops/sec = 800,876

  6. $('#' + $(Element).data('select')).text();

    @guest271314
    (Demo)
    characters = 42
    ops/sec = 47,144

  7. $('td:first-child',Element.parentNode.parentNode).text();

    @Tiny Giant
    (Demo)
    characters = 57
    ops/sec = 18,305

  8. $(Element).parent().siblings(":eq(0)").text();

    @guest271314
    (Demo)
    characters = 46
    ops/sec = 17,633

  9. $(Element).closest('tr').find('td:first').text();

    @j08691
    (Demo)
    characters = 49
    ops/sec = 4,511

  10. $(Element).parentsUntil('table').find('td:first').text();

    @AlvaroMontoro
    (Demo)
    characters = 54
    ops/sec = 3,954

The first five methods all use vanilla javascript where the last five use jQuery. There is a very significant drop in ops/sec in even the fastest jQuery solution. The difference between the slowest vanilla method vs the fastest jQuery method puts the jQuery method at 1/17th the speed of the vanilla method.

As you can see the accepted answer is the second slowest. I would caution against the use of closest in this situation. The more specific you can be the faster it will run, and closest just turns it into a guessing game.

This just goes to show, while it may take a little bit more time to type, it will always be faster to use vanilla javascript. What's even better, vanilla javascript is included by default!

$('#file2Download').val( 
    $(this).closest('tr').find('td:first').text()
);

.closest() will take you up the DOM from the button to the closest table row (<tr>), then .find('td:first').text() will search down the DOM and take the first table cell's text contents.

BTW, I assume you didn't mean $('#id0') in your example since your form's ID is form0, which would be selected via $('#form0').

The most elegant and efficient way to get the requisite grandparent's first child is to use plain old javascript. In this particular case, you already know exactly where to get the value you want. Just go get it by explicitly navigating the DOM on your own:

$("button").click(function(e) {

  var el = this         // button
    .parentNode         // parent
    .parentNode         // grandparent
    .firstElementChild; // grandparent's first child
  
  console.log(el.textContent);

});
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr>
    <td>someimage.png</td>
    <td><button type="button">Click to Download</button></td>
  </tr>
  <tr>
    <td>somedocument.docx</td>
    <td><button type="button">Click to Download</button></td>
  </tr>
</table>

That's about as fast as you'll ever get. Comparable jQuery code is actually much, much slower, i.e.: ≤ 4.2% of the speed. All you'd maybe gain from a jQuery solution is a few characters. The moral of the story is, don't be afraid to use vanilla javascript in your jQuery environment. Use either or both where it makes sense.

Now, for cases in which you're unsure of your target element's position in the reference element's ancestry, check out closest():

For each element in the set, get the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree.

var text = $(this).closest("tr").find("td:first-child").text();

$("button").click(function(e) {
  var text = $(this).closest("tr").find("td:first-child").text();
  console.log(text);
});
<script src="https://ajax.googleapis./ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
  <tr>
    <td>someimage.png</td>
    <td><button type="button">Click to Download</button></td>
  </tr>
  <tr>
    <td>somedocument.docx</td>
    <td><button type="button">Click to Download</button></td>
  </tr>
</table>

Let's shed light on the matter that can arise when thinking of the most reliable & efficient way. I will briefly write here about the closest() and parent() functions, so as to make it clear to any reader which to use, when and why.

closest() gets the first element that matches the selector, a jQuery object which we pass onto the function, by testing the given jQuery object and traversing up the DOM tree looking for its ancestors.

parent() travels a single element up the DOM tree and selects the immediate element who is parent to the given jQuery object.

Their difference is significant, despite the fact that they both seem to function similarly as how they traverse up the DOM tree.

Differences:

closest() - Begins with the current element. Travels up the DOM tree until it finds a match for the supplied selector. The returned jQuery object contains zero or one element for each element in the original set, in document order

parent() - Begins with the parent element. Travels up the DOM tree to the document's root element, adding each ancestor element to a temporary collection; it then filters that collection based on a selector if one is supplied. The returned jQuery object contains zero or more elements for each element in the original set, in reverse document order.

Therefore, parent would putationally provide a faster search due to its nature of going up a level into the parent tree structure. Keep in mind that this is the case since we're looking to find the grandparent.

Please refer to the jQuery documentation for more details on implementation.

$('#file2Download').val(
  $(this).parent().siblings(":eq(0)").text()
);        

$('button').click(function () {
        $('#file2Download').val(
           $(this).parent().siblings(":eq(0)").text()
        );        
      console.log($("#file2Download").val())
      //$('#id0').submit();
    });
<script src="https://ajax.googleapis./ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<form action="" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#masterLinkHolder" id="form0" method="post"></form>
  <table>
      <tr>
          <td>someimage.png</td>
          <td><img src="imageicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
      <tr>
          <td>somedocument.docx</td>
          <td><img src="docicon.png"></td>
          <td><button type="button">Click to Download</button></td>
      </tr>
  </table>
  <input type="hidden" id="file2Download" />
</form>


Note, See https://stackoverflow./a/30336313/

An alternative would be to add a data-* attribute to button elements , an id to first td ; could then select first td directly based on clicked button data-id="first-td"

html

<form action="/AssetBundle/DownloadFile" data-ajax="true" data-ajax-method="POST" data-ajax-mode="replace" data-ajax-update="#masterLinkHolder" id="form0" method="post"></form>
  <table>
      <tr>
          <td id="select1">someimage.png</td>
          <td><img src="imageicon.png"></td>
          <td><button type="button" data-select="select1">Click to Download</button></td>
      </tr>
      <tr>
          <td id="select2">somedocument.docx</td>
          <td><img src="docicon.png"></td>
          <td><button type="button" data-select="select2">Click to Download</button></td>
      </tr>
  </table>
  <input type="hidden" id="file2Download" />
</form>

js

$("#file2Download").val(document.getElementById(this.dataset.select).textContent)

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论