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

javascript - Can I rely on the implicit creation of the `<tbody>` tag? - Stack Overflow

programmeradmin0浏览0评论
<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script src=".6.3/jquery.min.js"></script>
  <script type="text/javascript">
    $( document ).ready( function(){
      $( "table > tr > td > input[id]" ).each( function( i, element ){ 
        alert( $( element ).attr( 'id' ) ) 
      });
    });
  </script>
</head>
<body>
  <form>
    <table>
      <tr><td>City:</td><td><input type="text" id="city" name="city" /></td></tr>
      <tr><td>state:</td><td><input type="text" id="state" name="state" /></td></tr>
    </table><br />
    <input type="submit" value="OK"/>
  </form>
</body>
</html>

When I write it this way, it doesn’t work because my browser automatically creates a <tbody> tag. So I have to write:

$( "table tr > td > input[id]" ).each( function( i, element ){ 
  alert( $( element ).attr( 'id' ) ) 
});

or:

$( "table > tbody > tr > td > input[id]" ).each( function( i, element ){ 
  alert( $( element ).attr( 'id' ) ) 
});

Can I rely on the implicit creation of the <tbody> tag, or should I not count on that?

Edit: added to explain my comment to Tim Down’s answer:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script src=".6.3/jquery.min.js"></script>
  <script src=".8.16/jquery-ui.js"></script>
  <script type="text/javascript">
    $( document ).ready( function() {
      var ids = [];
      var form = document.forms[0];
      var formEls = form.elements;
      var f_len = formEls.length;

      for ( var i = 0; i < f_len; ++i ) {
        ids.push( formEls[i].id );
      }

      var data = [ [ 'one', 'two', 'thre' ], [ 'four', 'five', 'six' ] ];
      var ids_len = ids.length;

      for ( i = 0; i < ids_len; i++ ){
        $( "#" + ids[i] ).autocomplete({
          source: data[i]
        });
      }
    });
  </script>
</head>
<body>
  <form>
    <table>
      <tr><td>A:</td><td><input type="text" id="a" name="a" /></td></tr>
      <tr><td>B:</td><td><input type="text" id="b" name="b" /></td></tr>
    </table><br />
    <input type="submit" value="OK"/>
  </form>
</body>
</html>

When I run this, the web console shows me a warning like this: Empty string to getElementById() is passed. One of the strings returned by form.elements is empty.

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
  <script type="text/javascript">
    $( document ).ready( function(){
      $( "table > tr > td > input[id]" ).each( function( i, element ){ 
        alert( $( element ).attr( 'id' ) ) 
      });
    });
  </script>
</head>
<body>
  <form>
    <table>
      <tr><td>City:</td><td><input type="text" id="city" name="city" /></td></tr>
      <tr><td>state:</td><td><input type="text" id="state" name="state" /></td></tr>
    </table><br />
    <input type="submit" value="OK"/>
  </form>
</body>
</html>

When I write it this way, it doesn’t work because my browser automatically creates a <tbody> tag. So I have to write:

$( "table tr > td > input[id]" ).each( function( i, element ){ 
  alert( $( element ).attr( 'id' ) ) 
});

or:

$( "table > tbody > tr > td > input[id]" ).each( function( i, element ){ 
  alert( $( element ).attr( 'id' ) ) 
});

Can I rely on the implicit creation of the <tbody> tag, or should I not count on that?

Edit: added to explain my comment to Tim Down’s answer:

<!DOCTYPE html>
<html>
<head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
  <script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
  <script type="text/javascript">
    $( document ).ready( function() {
      var ids = [];
      var form = document.forms[0];
      var formEls = form.elements;
      var f_len = formEls.length;

      for ( var i = 0; i < f_len; ++i ) {
        ids.push( formEls[i].id );
      }

      var data = [ [ 'one', 'two', 'thre' ], [ 'four', 'five', 'six' ] ];
      var ids_len = ids.length;

      for ( i = 0; i < ids_len; i++ ){
        $( "#" + ids[i] ).autocomplete({
          source: data[i]
        });
      }
    });
  </script>
</head>
<body>
  <form>
    <table>
      <tr><td>A:</td><td><input type="text" id="a" name="a" /></td></tr>
      <tr><td>B:</td><td><input type="text" id="b" name="b" /></td></tr>
    </table><br />
    <input type="submit" value="OK"/>
  </form>
</body>
</html>

When I run this, the web console shows me a warning like this: Empty string to getElementById() is passed. One of the strings returned by form.elements is empty.

Share Improve this question edited Jun 2, 2020 at 12:16 Sebastian Simon 19.5k8 gold badges60 silver badges84 bronze badges asked Sep 10, 2011 at 14:04 sid_comsid_com 25.1k27 gold badges102 silver badges191 bronze badges 2
  • 3 You'd save yourself a lot of headaches by simply adding an id to the form and then looping through the HTMLFormElement.elements collection. Please note that hideous queries like those in your post are very slow and problematic. – user1385191 Commented Sep 10, 2011 at 16:32
  • Closely related: stackoverflow.com/questions/938083/… . Answers there will also explain the spec. – Ciro Santilli OurBigBook.com Commented Jul 20, 2014 at 11:33
Add a comment  | 

6 Answers 6

Reset to default 7

It's not a question on relying on it being automatically created or not.

The question is if it's mandatory or not.

According to the HTML5 draft:

A tbody element's start tag may be omitted if the first thing inside the tbody element is a tr element, and if the element is not immediately preceded by a tbody thead, or tfoot element whose end tag has been omitted.

A tbody element’s end tag may be omitted if the tbody element is immediately followed by a tbody or tfoot element, or if there is no more content in the parent element.

So you can actually omit it if your code met the above conditions, otherwise it is needed.

As other people pointed out, even if it is needed, and the html parser won't find it because you didn't write it, it will be inserted into the DOM for you, as stated in the html5 specs.

This said, as a rule of thumb, never rely on anyone creating something automatically for you! (see below)

So even if the browser will create it for you, this doesn't mean newer browsers or new version of the same browser will follow the same way, and your code may become broken then.


Also, your JS could be optimized.

$( document ).ready( function(){
    $( "td > input[id]" ).each( function( i, element ){ 
        alert( element.id );
    });
});
  1. Always write semicolons at the end of statements. Don't rely on the JS engine write them for you!!! (see above).

  2. No need to call the jQuery function and create a jQuery object out of element just to call the attr() method to get the id. JavaScript already has the id() method to retrieve the id.

  3. If your actual markup is like the one you posted in your answer, you could write the jQuery selector like this: table input[id]. Or, if you have nested tables td > input[id] like gilly3 suggested.

I disagree with @spike's answer, at least if we talk about ordinary HTML parsing (not using innerHTML). Every tr becomes a child of implicitly created tbody unless it's already a child of another thead, tbody or tfoot. jQuery.support.tbody is for tables created using innerHTML or probably another DOM methods or so. If you omit <tbody> in the markup, it's always inserted.

The tbody element is not optional, it just has optional both opening and closing tag. To doubt about implicit tbody creation is similar mistake as to doubt about implicit creation of html or body element.

To prove what I said, the HTML4 specification forbids any <tr> elements as a direct childs of <table>s:

<!ELEMENT TABLE - -
 (CAPTION?, (COL*|COLGROUP*), THEAD?, TFOOT?, TBODY+)>

HTML5 specification says that <tr> can be, under certain circumstances, a child of <table>, however this applies only to DOM, not to markup parsing:

8.2.5.4.9 The "in table" insertion mode

...

A start tag whose tag name is one of: "td", "th", "tr"

Act as if a start tag token with the tag name "tbody" had been seen, then reprocess the current token.

You can't rely on the browser automatically creating it. The HTML spec says that it should be optional, though I believe that Firefox and IE create it as you saw. You can use this property to find out how the browser behaves (true means it won't be added)

jQuery.support.tbody

Check out this example in a bunch of browsers: http://jsfiddle.net/CuBX9/1/

http://api.jquery.com/jQuery.support/

To defend against the optional nature of the tbody tag (and by extension whatever the browser decides to do with its selector engine), you could write both selectors out:

$('table > tbody > tr > td, table > tr > td').find('input[type="text"]')

But that's honestly a bit of a hack. You'd be better off explicitly adding the <tbody> element and being done with it.

Alternatively, consider why you're even using the child combinator at all.

In your example, I don't know why you'd need to use such a complicated selector. You're not nesting tables (so no need to use child combinator), and you only have two text inputs. If all you want are the two text input fields, just use $('input[type="text"]').

You could just use the descendant selector instead of the parent selector, or some combination if it's important that the input be a child of a td. That way it wouldn't matter. Conversely, you could also just put the tbody elements in and use the full parent/child chain without worry.

$('table td > input[id]') 

If you're trying to get hold of all inputs within the <form> element that contains the table, this is the wrong approach, since there is a simple DOM approach that has existed since the dawn of JavaScript and works in all major scriptable browsers ever released. A form element has an elements property which is a collection of all the form controls within the form. All you need is to get hold of the form, which you can do in whichever way is most appropriate for you:

var form = document.forms[0];
var formEls = form.elements;
for (var i = 0, len = formEls.length; i < len; ++i) {
    alert(formEls[i].id);
}
发布评论

评论列表(0)

  1. 暂无评论