whatwg / html

HTML Standard
https://html.spec.whatwg.org/multipage/
Other
8.01k stars 2.62k forks source link

Mark tfoot before tbody as obsolete but conforming feature #352

Closed takenspc closed 8 years ago

takenspc commented 8 years ago

HTML 4 defined that

TFOOT must appear before TBODY within a TABLE definition

It is, however, hard to implement tfoot before tbody in accessible way. There are still issues around tfoot before tbody (see issues).

On the other hand HTML Standard has allowed placing tfoot after tbody. So let's discourage authors from using tfoot before tbody and encourage authors to use tfoot after tbody (see the proposal 1 and 2).

Issues

keyboard focus order

Let's consider a following table:

<table>
 <thead>
  <tr>
   <th scope="row">Year
   <th id="y2014">2014
   <th id="y2015">2015
 <tfoot>
  <tr>
   <th><a href="#help-gross-margin">Gross margin percentage</a>
   <td><output for="net-y2014 cost-y2014">34.3%</output>
   <td><output for="net-y2015 cost-y2015">34.0%</output>
 <tbody>
  <tr>
   <th id="net"><a href="help-net">Net sales</a>
   <td><input id="net-y2014" aria-labelledby="net y2014">
   <td><input id="net-y2015" aria-labelledby="net y2015">
  <tr>
   <th id="cost"><a href="help-cost">Cost of sales</a>
   <td><input id="cost-y2014" aria-labelledby="cost y2014">
   <td><input id="cost-y2015" aria-labelledby="cost y2015">
</table>

In this table, the keyboard focus moves tfoot then tbody in browsers. More specifically, the focus moves as follows:

  1. "Gross margin percentage" in tfoot
  2. first input in tbody
  3. second input in tbody

However tfoot is rendered after (usually below) tbody. It can be said that the visual order doesn't match the keyboard focus order. This could lead to confusion.

I admit that it is possible to introduce a sequential focus navigation algorithm for tables. But I belive that encouraging authors to use tfoot after tbody is more productive.

Order of Accessibility Objects

Tables with tfoot before tbody

Internet Explorer, Edge and Firefox exposed table rows to assistive technologies in following order:

  1. rows of thead
  2. rows of tfoot
  3. rows of tbody

while Chrome and Safari exposed rows in following order:

  1. rows of thead
  2. rows of tbody
  3. rows of tfoot

The difference affects reading order of screen readers. For example, NVDA + Firefox reads tfoot then tbody while NVDA + Chrome read tbody then tfoot.

Tables with tfoot after tbody

Above mentioned browsers exposed rows in following order:

  1. rows of thead
  2. rows of tbody
  3. rows of tfoot

Though the HTML Accessibility API Mappings may be responsible for that issue, I think that the HTML Standard should encourage authors to use tfoot after tbody.

Proposal 1

Move tfoot before tbody to the "obsolete but conforming features" from the main part of the specification.

Change the "content model" of table element as follows:

In this order: optionally a caption element, followed by zero or more colgroup elements, followed optionally by a thead element, followed by either zero or more tbody elements or one or more tr elements, followed optionally by a tfoot element, optionally intermixed with one or more script-supporting elements.

Change the "contexts in which this element can be used" of tfoot element as follows:

As a child of a table element, after any caption, colgroup, thead, tbody, and tr elements, but only if there are no other tfoot elements that are children of the table element.

Add following to "Warnings for obsolete but conforming features":

  • The presence of tfoot element ahead of tbody element.

    Proposal 2

The createTFoot() and tFoot setter insert tfoot before tbody in some circumstances.

I think it is too late to change to the behaviors of createTFoot() and tFoot setter although I'm skeptical that createTFoot and tFoot setter are used in the wild.

I propose making user agents to report warnings.

Add a note to tFoot and createTFoot() such as:

Inserting tfoot element before tbody element violates the content model of table element. The user agent should report warnings when a new tfoot element is going to be created.

foolip commented 8 years ago

This is somewhat related to https://www.w3.org/Bugs/Public/show_bug.cgi?id=29018

If there was a single row ordering that is used for the DOM APIs, for rendering and also for keyboard focus order, would that fix the problem? Any markup where the document order ends up not matching that order could be said to be invalid, which would include having tfoot before tbody.

takenspc commented 8 years ago

If there was a single row ordering that is used for the DOM APIs, for rendering and also for keyboard focus order, would that fix the problem?

Yes, it will fix the problems.

However I don't think that changing the DOM APIs and the keyboard focus order depending on the rendering order is a good solution. I worry that it will be hard to define the order of rows with display:none, display:block, display:flex and so on.

I think that theDOM order should be the single row ordering which is used for the DOM APIs and the keyboard focus order. Placing tfoot after tbody can achieve that. The rendering order will differ from the DOM order sometimes but match by default.

foolip commented 8 years ago

I don't mean that the DOM API or keyboard focus order would depend on the layout tree, just that the DOM API be made to match the reordering that happens in layout, and that keyboard focus order should match the DOM API.

What is the current keyboard focus order, by the way? Is it just the trs in tree order?

takenspc commented 8 years ago

I don't mean that the DOM API or keyboard focus order would depend on the layout tree, just that the DOM API be made to match the reordering that happens in layout, and that keyboard focus order should match the DOM API.

Sorry, I misunderstood. However please explain my concern. I worry that reordering happens multiple times. For example, some authors use display:block for table and related elements on narrow screens and not on wide screens. In that case, the return value of HTMLTableElement.rows could mutate without DOM operations.

Let's consider a following example.

var tFootBeforeTBodyTable = document.querySelector('table');
var rows = tFootBeforeTBodyTable.rows;
// rows -> [ first row of thead, ... , first row of tbody, ... , first row of tfoot]

Then the window was resized or the device orientation was changed and following styles were applied.

@media (...) {
    table, tbody { display: block; }
    thead, tfoot { display: none; }
}

The rows will mutate without DOM operations.

// rows -> [ first row of thead, ... , first row of tfoot, first row of tbody]

I think this is confusing behavior.


What is the current keyboard focus order, by the way? Is it just the trs in tree order?

Yes it is. AFAIK, display: table-*-group doesn't affect keyboard focus order in the browsers. Here is an example. http://codepen.io/takenspc/full/NGZMpy/

foolip commented 8 years ago

Actually, the definition of HTMLTableElement.prototype.rows depends only on the element types and not on computed style or the layout.

Even so, this is confusing, there's reordering here that tries to match the layout but doesn't really, not all of the time.

I'm quite unsure what to do about the DOM API here, it would probably be more sane if it just returned the rows in tree order with no reordering.

Making tfoot before tbody non-conforming sounds good to me, though. @sideshowbarker?

domenic commented 8 years ago

It seems a little unfortunate that we change authoring conformance requirements based on bugs in a11y tools. But I am happy to defer to our conformance checker expert @sideshowbarker.

foolip commented 8 years ago

Well, in this case it just seems like a misfeature that there should be any reordering anywhere, so if the reordering during layout is required for compat (which it probably is) it seems OK to just disallow any other order in the DOM.

Why it ever made sense to put the footers before the content I don't know, it seems like the idea might have been that headers and footers could be shown in multiple places for long tables, like some software does.

sideshowbarker commented 8 years ago

Why it ever made sense to put the footers before the content I don't know, it seems like the idea might have been that headers and footers could be shown in multiple places for long tables, like some software does.

Yeah, I believe that’s the rationale.

It seems a little unfortunate that we change authoring conformance requirements based on bugs in a11y tools.

Yeah, agreed. But as far as the behavior of the checker goes, it’s intended to help author-developers catch markup cases that might cause actual problems for end users in practice. From what @takenspc has described here, this seems like one of of those kinds of cases—that reality is that can cause problems, and we want to be able to make authors aware that it might.

So I do think it would be useful for the spec to include some requirement here to help authors avoid the problem and to guide checker developers. But that said, we could make it a “should”-level requirement rather than a “should”-level requirement. Like most document-conformance requirements, it’s a bit of judgement call. The checker behavior for a “should”-level requirement would just be a warning rather than an error, but in the end most author-devs using the checker tend to take warnings just as seriously as they do errors.

Given all that, I would tend to favor adding a statement to the spec like this:

A table should not contain a tfoot element before a tbody element [nor before the first tr element in the table?], because [user agents do not interoperability implement consistent behavior for how the resulting table is exposed to users of assistive technology such as screen readers].

And I would fine with elevating that “should” to a ”must” if we have agreement a stronger requirement is appropriate here.

foolip commented 8 years ago

So it's the content model of table that we should change. It's currently:

In this order: optionally a caption element, followed by zero or more colgroup elements, followed optionally by a thead element, followed optionally by a tfoot element, followed by either zero or more tbody elements or one or more tr elements, followed optionally by a tfoot element (but there can only be one tfoot element child in total), optionally intermixed with one or more script-supporting elements.

I think just removing the first "followed optionally by a tfoot element" part should do the trick. Is anything else needed?

zcorpan commented 8 years ago

That would make it "must".

We should check if it's possible to change the DOM API instead of assuming it's isn't.

zcorpan commented 8 years ago
SELECT page, url, body FROM [httparchive:runs.2014_08_15_requests_body]
WHERE REGEXP_MATCH(body, r'\.(tFoot\s*=|createTFoot\s*\()')

64 rows.

With response bodies: https://console.developers.google.com/m/cloudstorage/b/zcorpan/o/tfoot.csv.gzip (6.25MB)

Just page+url:

page    url
http://www.corcoran.com/    http://www.corcoran.com/Content/js/Scripts?v=rrmJuNTprMRptd4DdehI8gqvrQ-wUUXVQ3H-D6-7CY41
http://www.odziez-bielizna.pl/  http://walutami.pl/widget/widget.php?currencies[]=EURPLN&currencies[]=USDPLN&currencies[]=CHFPLN&currencies[]=GBPPLN&currencies[]=EURUSD&columns[]=Name&columns[]=Bid&columns[]=Ask
http://www.fcstats.com/ http://fcstats.com/js/libs.js
http://www.filegir.com/ http://www.filegir.com/classes/sorttable.js
http://www.ddl.me/  http://en.ddl.me/js/app-v0.2.js
http://www.easywls.org/ http://www.easywls.org/client/assets/71585c3e5858ef1d/stack/en_US/SearchTemplateStack.js
http://www.codeavengers.com/    http://www.codeavengers.com/jquery/js/plugins.min.js?9.12.0
http://www.anunciando.com.co/   http://www.anunciando.com.co/anunciando/deferredjs/6855CEE52397B15519A2A0E446D69B6C/1.cache.js
http://www.ddl.me/  http://en.ddl.me/js/mocha.js
http://www.itdistri.com/    http://itdistri.com/js/sorttable.js
http://www.programma.tv/    http://www.programma.tv/3.001/b/ample-0.9.5.a.2011.10.09.js
http://www.shredderchess.net/   https://www.shredderchess.net/webclient/8AEC10A3421C67CB31376E1C75988C96.cache.html
http://www.notatek.pl/  http://notatek.pl/components/platform/platform.js
http://www.dotamax.com/ http://www.dotamax.com/static/js/sorttable.js
http://www.hockey-reference.com/    http://d2ft4b0ve1aur1.cloudfront.net/js-412/sr-hr-min.js.jgz
http://www.titanshare.to/   http://titanshare.to/nload/21E40A8F21BB42D54BD9994F93236761.cache.html
http://www.advantageaustria.org/    http://www.advantageaustria.org/components/min/g=js&1406900702
http://www.pytajnia.pl/ http://www.pytajnia.pl/js/mintAjax.js
http://www.fr.pl/   http://ecenter.pl/js/sortable.js
http://www.netstate.com/    http://www.netstate.com/includes/js/script.js
http://www.componentone.com/    http://www.componentone.com/ScriptResource.axd?d=KwlBz2fHX-Xdo4oLLmgX7xSfn5hOz_h5o61qvndM0UBYfCxtrkIBNQ-npbq52JnQLUcfGdI3t0-TshX9THhvsMmxX-sb5klij3vllh6Y4ivVEZcIf_ZERWKbIt2rO6HIz0nOiun6PzEy8cuqCI5-DAORsRYpK8D1pVyFRQKfmtlevWxp0&t=542fdfb0
http://www.vkdownload.net/  http://www.vkdownload.net/js/sortable.js
http://www.phila.gov/   http://cdn.wijmo.com/jquery.wijmo-complete.all.2.3.2.min.js
http://www.statsf1.com/ http://www.statsf1.com/include/master.js
http://www.emaluszki.pl/    http://emaluszki.pl/libs/mint.js
http://www.fifa.com/    http://js.fifa.com/components/script/require-libs/frameworks/bundle.js?v=635436162292838476
http://www.goo.gl/  http://goo.gl/gwt/A4360D16C0B7553E09A22F16D0F3584A.cache.js
http://www.pro-football-reference.com/  http://d2ft4b0ve1aur1.cloudfront.net/js-412/sr-pfr-min.js.jgz
http://www.srvusd.net/  http://cdn.schoolloop.com/1408140958/static/lib/wijmo/Wijmo.2.3.8/Wijmo-Complete/js/jquery.wijmo-complete.all.2.3.8.min.js
http://www.centralazabawek.pl/  http://www.centralazabawek.pl/scripts/mintajax.js
http://www.coinmine.pw/ http://www.coinmine.pw/pics/sorttable.js
http://www.tokinito.gr/ http://tokinito.gr/js/jquery.wijmo-pro.all.3.20131.1.min.js
http://www.wijmo.com/   http://wijmo.com/wp-content/themes/wijmov2/js/jquery.wijmo-pro.all.3.20141.34.min.js?ver=20141.34
http://www.canlifm.com/ http://canlifm.com/radyodinle/views/theme/js/sorttable.js
http://www.cooperindustries.com/    http://www.cooperindustries.com/apps/public/docroot/scripts/sorttable.js
http://www.richfaces.org/   https://www.jboss.org/.resources/jbossorg-downloads2/sorttable.js
http://www.colubris.com/    http://h17007.www1.hp.com/tridion/assets/scripts/sortableTable-hpe.js
http://www.myunfairadvantage.net/   http://cdn.wijmo.com/jquery.wijmo-pro.all.3.20133.20.min.js
http://www.barcamp.org/ http://vs1.pbworks.com/shared/statics/packed-v65464171.js
http://www.fohrcard.com/    http://www.fohrcard.com/assets/application-e4c80d88a9658a8ad6d3e7ae54370c6a.js
http://www.sports-reference.com/    http://d2ft4b0ve1aur1.cloudfront.net/js-402/sr-base-min.js.jgz
http://www.usahockey.com/   http://app-assets3.sportngin.com/javascripts/base_packaged.js?1408126954
http://www.fastenal.com/    http://www.fastenal.com/web/static/scripts/scripts-9.19.6-min.js
http://www.halotracker.com/ http://www.halotracker.com/htr.js?V=16
http://www.worldpopulationreview.com/   http://worldpopulationreview.com/js/concat.js
http://www.customelements.io/   http://customelements.io/bower_components/platform/platform.js
http://www.skyscrapercenter.com/    http://www.skyscrapercenter.com/jquery.tablesorter/sorttable.js
http://www.launchpad.net/   https://launchpad.net/+combo/rev17156/?yui/array-extras/array-extras-min.js&yui/anim-base/anim-base-min.js&yui/anim-color/anim-color-min.js&yui/anim-xy/anim-xy-min.js&yui/anim-curve/anim-curve-min.js&yui/anim-easing/anim-easing-min.js&yui/anim-node-plugin/anim-node-plugin-min.js&yui/anim-scroll/anim-scroll-min.js&lp/app/javascript/effects/effects-min.js&lp/app/javascript/expander-min.js&lp/app/javascript/lp-min.js&lp/app/javascript/foldables-min.js&lp/app/javascript/sorttable/sorttable-min.js&yui/widget-stdmod/widget-stdmod-min.js&yui/widget-position/widget-position-min.js&yui/widget-position-align/widget-position-align-min.js&yui/widget-stack/widget-stack-min.js&yui/widget-position-constrain/widget-position-constrain-min.js&yui/overlay/overlay-min.js&lp/app/javascript/overlay/overlay-min.js&yui/querystring-stringify-simple/querystring-stringify-simple-min.js&yui/io-base/io-base-min.js&yui/datatype-xml-parse/datatype-xml-parse-min.js&yui/io-xdr/io-xdr-min.js&yui/io-form/io-form-min.js&yui/io-upload-iframe/io-upload-iframe-min.js&yui/queue-promote/queue-promote-min.js&yui/io-queue/io-queue-min.js&lp/app/inlinehelp/inlinehelp-min.js&yui/json-parse/json-parse-min.js&yui/json-stringify/json-stringify-min.js&yui/querystring-parse/querystring-parse-min.js&yui/querystring-stringify/querystring-stringify-min.js&lp/app/javascript/client-min.js&lp/app/javascript/lp-links-min.js&lp/app/javascript/longpoll-min.js&lp/app/javascript/mustache-min.js&lp/app/javascript/formoverlay/formoverlay-min.js&lp/app/javascript/extras/extras-min.js&lp/app/javascript/anim/anim-min.js&lp/app/javascript/choiceedit/choiceedit-min.js&yui/dump/dump-min.js&lp/app/javascript/ui/ui-min.js&lp/app/javascript/activator/activator-min.js&yui/escape/escape-min.js&yui/plugin/plugin-min.js&lp/app/javascript/picker/picker-min.js&lp/app/javascript/client-min.js&yui/event-simulate/event-simulate-min.js&yui/async-queue/async-queue-min.js&yui/gesture-simulate/gesture-simulate-min.js&yui/node-event-simulate/node-event-simulate-min.js
http://www.esselungajob.it/ http://www.esselungajob.it/lavoraconnoi/it.esselunga.hrweb.Application/3C01EBFCC563833BAD3290D8F2214C1D.cache.html
http://www.maximintegrated.com/ http://www.maximintegrated.com/etc/designs/maximintegrated/clientlibs/common.js
http://www.browserscope.org/    http://www.browserscope.org/static/browserscope.js?v=21.375139290952075791
http://www.sportngin.com/   http://app-assets3.sportngin.com/javascripts/base_packaged.js?1408126954
http://www.goddiva.co.uk/   http://www.goddiva.co.uk/core/media/media.nl?id=175754&c=949266&h=4c34893c66ba5e166a1c&_xt=.js
http://www.polymer-project.org/ http://www.polymer-project.org/platform.js?20140808
http://www.hilltopads.net/  http://www.hilltopads.net/js/sortable.js
http://www.esselungajob.it/ http://www.esselungajob.it/lavoraconnoi/it.esselunga.hrweb.Application/3C01EBFCC563833BAD3290D8F2214C1D.cache.html
http://www.instantsoftwareonline.com/   https://apex.cherwellondemand.com/CherwellPortal/IT/Resource/Script/scripts.jquery.wijmo-complete.all.2.3.2.min.js
http://www.basketball-reference.com/    http://d2ft4b0ve1aur1.cloudfront.net/js-412/sr-bbr-min.js.jgz
foolip commented 8 years ago

We should check if it's possible to change the DOM API instead of assuming it's isn't.

As discovered by Prashant, it's not entirely interoperable right now, so I think the chances of it being possible to change are pretty good.

What should we try to measure? All cases where the rows collection is used and there's a thead element after any tr/tbody, or where there's a tfoot before any tr/tbody?

zcorpan commented 8 years ago

I was talking about tFoot setter and createTFoot() method (if they can insert the tfoot last), not rows.

zcorpan commented 8 years ago

tFoot setter http://software.hixie.ch/utilities/js/live-dom-viewer/saved/3776 createTFoot() http://software.hixie.ch/utilities/js/live-dom-viewer/saved/3777

It looks like Firefox inserts them last. I suppose that makes it pretty safe to change.

zcorpan commented 8 years ago

OK so for this bug I suggest

(Making the tfoot content model "should" makes the spec more complicated instead of simpler, which seems bad.)

domenic commented 8 years ago

In the absence of any authors actually wanting to put tfoot before tbody I agree with that plan. Personally I think in theory it could be nice ("meta stuff first, then main stuff") but I admit never having actually written a tfoot so I'm fine with the restriction.

foolip commented 8 years ago

So what about the rows ordering in https://www.w3.org/Bugs/Public/show_bug.cgi?id=29018? Some change seems necessary, so if we change the conformance and content that results in reordering is extremely rare, shouldn't we simplify it?

zcorpan commented 8 years ago

Let's discuss rows in that bug.

domenic commented 8 years ago

It sounds like we have a plan: https://github.com/whatwg/html/issues/352#issuecomment-162011747

Who wants to make this change? @takenspc? @zcorpan?

Ideally when doing this people would write web platform tests for the tFoot setter and createTFoot() method.