Bug Tracker

Modify

Ticket #4512 (closed bug: fixed)

Opened 5 years ago

Last modified 2 years ago

jquery-1.3.2.min.js "visible" selector doesn't run properly

Reported by: qiuzj Owned by: snover
Priority: high Milestone: 1.4.3
Component: css Version: 1.4.2
Keywords: Cc: dmethvin, snover
Blocking: Blocked by:

Description

HTML Code: <table>

<tr style="display:none"><td>Value 1</td></tr> <tr><td>Value 2</td></tr>

</table>

jQuery Code: $("tr:visible")

Results: [ <tr style="display:none"><td>Value 1</td></tr>,<tr><td>Value 2</td></tr> ]

Attachments

test2.html Download (1.2 KB) - added by brandon 5 years ago.
4512.diff Download (937 bytes) - added by brandon 5 years ago.
test-all-elements.html Download (12.8 KB) - added by jvdneut 4 years ago.
Test page for IE8 visibility problem

Change History

comment:1 Changed 5 years ago by qiuzj

Firefox: [ <tr><td>Value 2</td></tr> ]

IE: [ <tr style="display:none"><td>Value 1</td></tr>,<tr><td>Value 2</td></tr> ]

comment:2 Changed 5 years ago by brandon

  • Owner set to john
  • Component changed from ajax to selector
  • Milestone changed from 1.3.2 to 1.3.3

comment:3 Changed 5 years ago by brandon

  • Owner changed from john to brandon

comment:4 Changed 5 years ago by brandon

  • Status changed from new to closed
  • Resolution set to invalid

I'm unable to reproduce this. Please validate your HTML. If you are able to create a complete and clean test case, please feel free to reopen this ticket.

Changed 5 years ago by brandon

comment:5 Changed 5 years ago by brandon

  • Status changed from closed to reopened
  • Resolution invalid deleted

Found a complete test-case that is failing.

comment:6 Changed 5 years ago by brandon

And realized my own test-case was flawed. I was looking at the td, not the tr.

The tr, although hidden with display: none, still has a width > 0. The height of the hidden tr is 0. The logic for the :visible/:hidden selectors was changed so that if an element has a width or a height, it is considered visible.

The reason it can have a height of 0 and width > 0 and still be visible is because a parent with uncleared floated elements is still visible but will have a height of 0.

comment:7 follow-up: ↓ 8 Changed 5 years ago by benoit@…

I just have a look on the source and I think the following code :

return elem.offsetWidth > 0 || elem.offsetHeight > 0;

should be replaced by :

return elem.offsetWidth > 0 && elem.offsetHeight > 0;
  • (jQuery/src/selector.js at line 984)
  • This works on firefox and ie6 event if the element is a TR

comment:8 in reply to: ↑ 7 Changed 5 years ago by brandon

Replying to benoit@gardentechno.:

I just have a look on the source and I think the following code :

return elem.offsetWidth > 0 || elem.offsetHeight > 0;

should be replaced by :

return elem.offsetWidth > 0 && elem.offsetHeight > 0;

Unfortunately an element can be "visible" even if one of its dimensions is 0. For example, a div that has un-cleared floated children will cause the parent's height to be 0 but the width will be > 0. Just because the height is 0 doesn't mean the element is hidden.

comment:9 Changed 5 years ago by brandon

My thinking right now is to use the width/height checks for speed and if the result is ambiguous (one dimension is 0 and the other is > 0) then check the display property.

Changed 5 years ago by brandon

comment:10 Changed 5 years ago by brandon

  • Status changed from reopened to closed
  • Resolution set to fixed

fixed in r6357

comment:11 Changed 5 years ago by vbabiy86

  • Status changed from closed to reopened
  • Resolution fixed deleted

I just checked out r6357 and it does not work in IE8 if I turn on compatibility mode it works fine.

comment:12 follow-up: ↓ 13 Changed 5 years ago by dmethvin

Can you expand on "does not work" by providing a test case? Thanks.

comment:13 in reply to: ↑ 12 Changed 5 years ago by brandon

Replying to dmethvin:

Can you expand on "does not work" by providing a test case? Thanks.

I can verify that the patch does not work with the supplied test case in IE8. There is a thread in the dev mailing-list where I've posted another possible solution.

comment:15 Changed 5 years ago by brandon

The other possible solution I posted was this.

Index: src/selector.js
===================================================================
--- src/selector.js     (revision 6357)
+++ src/selector.js     (working copy)
@@ -977,19 +977,21 @@
 jQuery.expr[":"] = jQuery.expr.filters;

 Sizzle.selectors.filters.hidden = function(elem){
-       var width = elem.offsetWidth, height = elem.offsetHeight;
-       return ( width === 0 && height === 0 ) ?
+       var width = elem.offsetWidth, height = elem.offsetHeight,
+               force = /tr/i.test( elem.tagName );
+       return ( width === 0 && height === 0 && !force ) ?
               true :
-               ( width !== 0 && height !== 0 ) ?
+               ( width !== 0 && height !== 0 && !force ) ?
                       false :
                       !!( jQuery.curCSS(elem, "display") === "none" );
 };

 Sizzle.selectors.filters.visible = function(elem){
-       var width = elem.offsetWidth, height = elem.offsetHeight;
-       return ( width === 0 && height === 0 ) ?
+       var width = elem.offsetWidth, height = elem.offsetHeight,
+               force = /^tr$/i.test( elem.tagName );
+       return ( width === 0 && height === 0 && !force ) ?
               false :
-               ( width > 0 && height > 0 ) ?
+               ( width > 0 && height > 0 && !force ) ?
                       true :
                       !!( jQuery.curCSS(elem, "display") !== "none" );
 };

comment:16 Changed 5 years ago by brandon

  • Status changed from reopened to closed
  • Resolution set to fixed

Fixed in r6413

comment:17 Changed 5 years ago by alex88

  • Status changed from closed to reopened
  • Resolution fixed deleted

The same problem occurs for divs etc too (no only TRs), you can see it here:

 http://jsbin.com/obavo

using ie8. Temp01 from irc channel suggested me this solution, replace:

force = /tr$/i.test( elem.nodeName ); ticket #4512

with:

force = true;

in nightly build rev. 6586, and now it's working fine.

comment:18 Changed 4 years ago by davidserduke

See duplicate #4512

comment:21 follow-up: ↓ 23 Changed 4 years ago by john

  • Status changed from reopened to closed
  • Resolution set to fixed

Looks like this fix is still working, as far as I can tell.

comment:23 in reply to: ↑ 21 Changed 4 years ago by januszjasinski

  • Status changed from closed to reopened
  • Resolution fixed deleted

Replying to john:

Looks like this fix is still working, as far as I can tell.

It is not working in IE8 as can be seen when going to  http://jsbin.com/ijini and when loading the file here:  http://dev.jquery.com/attachment/ticket/4512/test2.html

comment:24 Changed 4 years ago by janusz

The only way I can get this to work is using the code below

$("#clicktoshoworhide").click(function() {  
var elem = $('#elementtoshoworhide')[0];
if(elem.style.display == 'none')
  $('#elementtoshoworhide').show();
    else
  {
    $('#elementtoshoworhide').hide();                       
}

comment:25 Changed 4 years ago by dmethvin

  • Status changed from reopened to closed
  • Resolution set to fixed

janusz, you're using jQuery 1.3.2 for your test and the fix is in the nightlies.

comment:26 Changed 4 years ago by cipher

  • Status changed from closed to reopened
  • Resolution fixed deleted

This is not fixed. In IE8 offsetWidth and offsetHeight are not zero for UL's as well.

I have tested the nightly jQuery file and is(':visible') does not work correctly (along with .toggle()) with UL's.

changing the code to: force = /^(?:tr|ul)$/i.test(...); has fixed the problem; I still don't like selectively testing certain elements for display:none. I think that if the elements height and width are 0, it's hidden; if not, it might be hidden but display:none should still be checked.

comment:27 follow-up: ↓ 48 Changed 4 years ago by john

  • Status changed from reopened to closed
  • Resolution set to fixed

@cipher. I created a test case but I got the result that I expected:

<script src="dist/jquery.js"></script>
<script>
$(window).load(function(){
        alert( $("ul:first").is(":hidden") );
        alert( $("ul:last").is(":hidden") );
});
</script>
<ul style="width:0;height:0;"></ul>
<ul style="display:none;"></ul>

The first one was false (height/width of zero, but still visible - correct) the second one was true (display: none, not visible).

Changed 4 years ago by jvdneut

Test page for IE8 visibility problem

comment:48 in reply to: ↑ 27 Changed 4 years ago by jvdneut

  • Status changed from closed to reopened
  • Resolution fixed deleted

Replying to john:

I have encountered the same problem using jQuery 1.4.2 running on IE8 in standards mode but for the TD and TH tags.

I wrote the following test code to try and detect all the elements that were causing problems. It is by no means complete, but it does show that the problem occurs on more elements than just TR's (specifically it also occurs on BUTTON, TD, TH, CODE, and Q)  http://dev.jquery.com/attachment/ticket/4512/test-all-elements.html

I also discovered that the problem only occurs on IE8 running in "standards mode", meaning that pages that do not specify a doctype or include a <meta http-equiv="X-UA-Compatible" content="IE=7" /> do work correctly.

The problem is, as stated earlier, related to the offsetWidth and offsetHeight of the elements in question not being equal to 0 when hidden.

For my own purposes I modified the jQuery file to read: skip = /(tr|td|th)$/.test(elem.nodeName.toLowerCase()); instead of: skip = elem.nodeName.toLowerCase() === "tr";

comment:49 Changed 4 years ago by snover

  • Cc dmethvin, snover added
  • need changed from Review to Patch
  • Priority changed from major to high
  • Version changed from 1.3.2 to 1.4.2
  • Milestone changed from 1.4 to 1.4.3

So, just to clarify, the *principal* (but not only) issue here is that IE gives table elements a width and height whenever there is an additional table cell in the row that is not hidden but which also has a height.

We can work around that. This bug is far more nefarious, however, and I feel like laughing and crying at the same time.

In doing research for this issue, I have discovered that are some inexplicable rules dictating when offsetWidth/offsetHeight are non-zero:

  1. button inside label (but not input)
  2. q inside q (but not code inside code, div inside div, or span inside span)
  3. any table cell as long as there is at least one table cell in the row that is not hidden and is not empty
  4. any element followed by an inline element as long as the first element in the parent is a text node

That last one is particularly interesting. As far as I can tell, none of these rules are related to hasLayout, either.

In conclusion, I think we can safely patch the issue with :visible selector and tables, but unless someone can figure out the voodoo that is going on behind the scenes in IE’s warped little brain, someone will eventually be haunted by this shortcut again in another permutation.

comment:50 Changed 4 years ago by snover

OK, so, modification to the rules, based off this  plainly ridiculous test case.

  1. Innermost nested q breaks if any of the parent q is followed by another node (other elements like span or code are OK)<br>
  2. Any td or th or tr as long as there is at least one table cell in the row that is not hidden and not empty<br>
  3. button preceeded by a button or non-block node<br>
  4. Some voodoo combinations of inline elements, which also sometimes affect block-level elements but not usually

If someone can figure out that voodoo, I will be super-duper impressed.

FML Trident is a mess! Interestingly, IE9b1 in IE8 and IE9 modes pass, but IE9b1 in IE9 mode required the addition of a check to avoid testing html/body/head/etc..

comment:51 Changed 4 years ago by snover

OK.

So, the reason that q appears on the surface to be special is because IE8 generates content. This is not immediately obvious in the above test-case because jsfiddle sets the content to "". That is not the same as setting the content to none, however, so the generated text nodes are still created. This makes q appear special but it is apparently not so much.

danheberden and I worked on this for a few hours, and the pattern seems to most closely match this description:

IE8 fails to return offsetWidth/Height properly for hidden elements whenever the element has inline siblings on both sides, or whenever the element is an only child and its parent has an inline next sibling. (Text nodes are apparently considered to be inline elements for the purposes of these decisions within Trident.)

I know what you are thinking—this sounds like a bug that one would expect to see from hasLayout—but forcing layout does not solve the problem, and the buggy elements are a mix of elements with and without layout. So, no luck there.

I think that the most important takeaway from this analysis is not the actual criteria under which IE8 screws up; rather, it is that the behaviour changes depending upon the existence or non-existence of generated content, which means that it appears there is no way of 100% reliably detecting the conditions under which a specific hidden element may not correctly return offsetWidth/Height.

Testing for this condition is very easy, and I have written a patch to detect it. Unfortunately, once we know that offsetWidth/Height is unreliable, it looks like we’ll be forced to walk up the DOM every time someone wants to determine if an element is hidden in IE8. :(

comment:52 Changed 4 years ago by snover

  • Owner changed from brandon to snover
  • Status changed from reopened to new

comment:53 Changed 4 years ago by snover

  • Status changed from new to assigned

comment:54 Changed 4 years ago by snover

  • need changed from Patch to Commit

I would just like to mention that the final statement in my last comment was wrong; offsetWidth/Height are only wrong when the element itself has display: none set. If an ascendant is display: none, then offsetWidth/Height are still zero. Yay!

 Pull request

comment:55 Changed 4 years ago by snover

#6895 is a duplicate of this bug.

comment:56 Changed 4 years ago by john

  • Status changed from assigned to closed
  • Resolution set to fixed

comment:57 Changed 4 years ago by john

  • Component changed from selector to css

comment:58 Changed 3 years ago by snover

#5670 is a duplicate of this ticket.

comment:59 Changed 3 years ago by snover

#5567 is a duplicate of this ticket.

Please follow the  bug reporting guidlines and use  jsFiddle when providing test cases and demonstrations instead of pasting the code in the ticket.

View

Add a comment

Modify Ticket

Action
as closed
Author


E-mail address and user name can be saved in the Preferences.

 
Note: See TracTickets for help on using tickets.