Permalink
Browse files

Remove the invisible body in support; Add temporary tests to verify c…

…orrect support completions for upcoming support changes.
  • Loading branch information...
1 parent 0de484d commit 3d6237ef8aff8a31ace3e956e2700aa11e3da752 @timmywil timmywil committed Nov 17, 2011
Showing with 331 additions and 103 deletions.
  1. +65 −101 src/support.js
  2. +1 −1 test/data/support/boxModelIE.html
  3. +1 −1 test/index.html
  4. +264 −0 test/unit/support.js
View
166 src/support.js
@@ -2,31 +2,26 @@
jQuery.support = (function() {
- var div = document.createElement( "div" ),
- documentElement = document.documentElement,
+ var support,
all,
a,
select,
opt,
input,
marginDiv,
- support,
fragment,
- body,
@mikesherov
mikesherov Nov 18, 2011

body leaks to global now, right? I found this with JSHint, btw. ;-)

- testElementParent,
- testElement,
- testElementStyle,
tds,
events,
eventName,
i,
- isSupported;
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
// Preliminary tests
div.setAttribute("className", "t");
div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
-
all = div.getElementsByTagName( "*" );
a = div.getElementsByTagName( "a" )[ 0 ];
@@ -46,19 +41,19 @@ jQuery.support = (function() {
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
- tbody: !div.getElementsByTagName( "tbody" ).length,
+ tbody: !div.getElementsByTagName("tbody").length,
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
- htmlSerialize: !!div.getElementsByTagName( "link" ).length,
+ htmlSerialize: !!div.getElementsByTagName("link").length,
// Get the style information from getAttribute
// (IE uses .cssText instead)
style: /top/.test( a.getAttribute("style") ),
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
- hrefNormalized: ( a.getAttribute( "href" ) === "/a" ),
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
// Make sure that element opacity exists
// (IE uses filter instead)
@@ -140,95 +135,28 @@ jQuery.support = (function() {
// WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
- div.innerHTML = "";
-
- // Figure out if the W3C box model works as expected
- div.style.width = div.style.paddingLeft = "1px";
-
- // We don't want to do body-related feature tests on frameset
- // documents, which lack a body. So we use
- // document.getElementsByTagName("body")[0], which is undefined in
- // frameset documents, while document.body isn’t. (7398)
- body = document.getElementsByTagName("body")[ 0 ];
- // We use our own, invisible, body unless the body is already present
- // in which case we use a div (#9239)
- testElement = document.createElement( body ? "div" : "body" );
- testElementStyle = {
- visibility: "hidden",
- width: 0,
- height: 0,
- border: 0,
- margin: 0,
- background: "none"
- };
- if ( body ) {
- jQuery.extend( testElementStyle, {
- position: "absolute",
- left: "-999px",
- top: "-999px"
- });
- }
- for ( i in testElementStyle ) {
- testElement.style[ i ] = testElementStyle[ i ];
- }
- testElement.appendChild( div );
- testElementParent = body || documentElement;
- testElementParent.insertBefore( testElement, testElementParent.firstChild );
-
// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
support.appendChecked = input.checked;
- support.boxModel = div.offsetWidth === 2;
-
- if ( "zoom" in div.style ) {
- // Check if natively block-level elements act like inline-block
- // elements when setting their display to 'inline' and giving
- // them layout
- // (IE < 8 does this)
- div.style.display = "inline";
- div.style.zoom = 1;
- support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
-
- // Check if elements with layout shrink-wrap their children
- // (IE 6 does this)
- div.style.display = "";
- div.innerHTML = "<div style='width:4px;'></div>";
- support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
- }
-
- div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
- tds = div.getElementsByTagName( "td" );
-
- // Check if table cells still have offsetWidth/Height when they are set
- // to display:none and there are still other visible table cells in a
- // table row; if so, offsetWidth/Height are not reliable for use when
- // determining if an element has been hidden directly using
- // display:none (it is still safe to use offsets if a parent element is
- // hidden; don safety goggles and see bug #4512 for more information).
- // (only IE 8 fails this test)
- isSupported = ( tds[ 0 ].offsetHeight === 0 );
-
- tds[ 0 ].style.display = "";
- tds[ 1 ].style.display = "none";
+ fragment.removeChild( input );
+ fragment.appendChild( div );
- // Check if empty table cells still have offsetWidth/Height
- // (IE < 8 fail this test)
- support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
div.innerHTML = "";
// Check if div with explicit width and no margin-right incorrectly
// gets computed margin-right based on width of container. For more
// info see bug #3333
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
- if ( document.defaultView && document.defaultView.getComputedStyle ) {
+ if ( window.getComputedStyle ) {
marginDiv = document.createElement( "div" );
marginDiv.style.width = "0";
marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
div.appendChild( marginDiv );
support.reliableMarginRight =
- ( parseInt( ( document.defaultView.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
@mikesherov
mikesherov Nov 17, 2011

excuse my ignorance, but doesn't FF3.6 need document.defaultView so IFRAME's will be happy? #524 (comment)

@timmywil
timmywil Nov 18, 2011

It is needed in css.js, not in the support test.

@rwaldron
rwaldron Nov 18, 2011

What if jQuery is loaded in an iframe that has display:none in Firefox 3.6? (I'm curious, I haven't tested this)

@mikesherov
mikesherov Nov 18, 2011

funny you should ask, @rwldrn, I have a PR for this sitting in the queue: #555

@jdalton
jdalton Nov 18, 2011

Ok, so this is kinda an interesting issue. Would ❤️ a blog post on it.

@timmywil
timmywil Nov 18, 2011

@rwldrn: Just tested and looks like it has the correct result.

}
// Technique from Juriy Zaytsev
@@ -242,7 +170,7 @@ jQuery.support = (function() {
submit: 1,
change: 1,
focusin: 1
- } ) {
+ }) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
@@ -253,11 +181,10 @@ jQuery.support = (function() {
}
}
- testElement.innerHTML = "";
- testElementParent.removeChild( testElement );
+ fragment.removeChild( div );
- // Null connected elements to avoid leaks in IE
- testElement = fragment = select = opt = body = marginDiv = div = input = null;
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = body = marginDiv = div = input = null;
@mikesherov
mikesherov Nov 18, 2011

body is not defined before this.

// Run fixed position tests at doc ready to avoid a crash
// related to the invisible body in IE8
@@ -268,8 +195,8 @@ jQuery.support = (function() {
vb = "visibility:hidden;border:0;",
style = "style='" + ptlm + "border:5px solid #000;padding:0;'",
html = "<div " + style + "><div></div></div>" +
- "<table " + style + " cellpadding='0' cellspacing='0'>" +
- "<tr><td></td></tr></table>";
+ "<table " + style + " cellpadding='0' cellspacing='0'>" +
+ "<tr><td></td></tr></table>";
// Reconstruct a container
body = document.getElementsByTagName("body")[0];
@@ -283,13 +210,53 @@ jQuery.support = (function() {
container.style.cssText = vb + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
body.insertBefore( container, body.firstChild );
- // Construct a test element
- testElement = document.createElement("div");
- testElement.style.cssText = ptlm + vb;
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "<table><tr><td style='padding:0;border:0;display:none'></td><td>t</td></tr></table>";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Figure out if the W3C box model works as expected
+ div.innerHTML = "";
+ div.style.width = div.style.paddingLeft = "1px";
+ jQuery.boxModel = support.boxModel = div.offsetWidth === 2;
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 2 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "";
+ div.innerHTML = "<div style='width:4px;'></div>";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 2 );
+ }
+
+ div.style.cssText = ptlm + vb;
+ div.innerHTML = html;
- testElement.innerHTML = html;
- container.appendChild( testElement );
- outer = testElement.firstChild;
+ outer = div.firstChild;
inner = outer.firstChild;
td = outer.nextSibling.firstChild.firstChild;
@@ -312,15 +279,12 @@ jQuery.support = (function() {
offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
body.removeChild( container );
- testElement = container = null;
+ div = container = null;
jQuery.extend( support, offsetSupport );
});
return support;
})();
-// Keep track of boxModel
-jQuery.boxModel = jQuery.support.boxModel;
-
})( jQuery );
View
2 test/data/support/boxModelIE.html
@@ -22,7 +22,7 @@
<script src="../../../src/offset.js"></script>
<script src="../../../src/dimensions.js"></script>
<script>
- window.parent.supportCallback( document.compatMode, jQuery.support.boxModel );
+ jQuery(function() { window.parent.supportCallback( document.compatMode, jQuery.support.boxModel ) });
</script>
</body>
</html>
View
2 test/index.html
@@ -36,9 +36,9 @@
<script src="data/testrunner.js"></script>
<script src="unit/core.js"></script>
- <script src="unit/support.js"></script>
<script src="unit/callbacks.js"></script>
<script src="unit/deferred.js"></script>
+ <script src="unit/support.js"></script>
<script src="unit/data.js"></script>
<script src="unit/queue.js"></script>
<script src="unit/attributes.js"></script>
View
264 test/unit/support.js
@@ -60,3 +60,267 @@ supportIFrameTest( "A background on the testElement does not cause IE8 to crash
expect(1);
ok( true, "IE8 does not crash" );
});
+
+var userAgent = window.navigator.userAgent;
+
+// These tests do not have to stay
+// They are here to help with upcoming support changes for 1.8
+if ( /chrome\/16\.0/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "leadingWhitespace":true,
+ "tbody":true,
+ "htmlSerialize":true,
+ "style":true,
+ "hrefNormalized":true,
+ "opacity":true,
+ "cssFloat":true,
+ "checkOn":true,
+ "optSelected":true,
+ "getSetAttribute":true,
+ "enctype":true,
+ "html5Clone":true,
+ "submitBubbles":true,
+ "changeBubbles":true,
+ "focusinBubbles":false,
+ "deleteExpando":true,
+ "noCloneEvent":true,
+ "inlineBlockNeedsLayout":false,
+ "shrinkWrapBlocks":false,
+ "reliableMarginRight":true,
+ "noCloneChecked":true,
+ "optDisabled":true,
+ "radioValue":true,
+ "checkClone":true,
+ "appendChecked":true,
+ "boxModel":true,
+ "reliableHiddenOffsets":true,
+ "ajax":true,
+ "cors":true,
+ "doesNotAddBorder":true,
+ "doesAddBorderForTableAndCells":false,
+ "fixedPosition":true,
+ "subtractsBorderForOverflowNotVisible":false,
+ "doesNotIncludeMarginInBodyOffset":true
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+} else if ( /msie 8\.0/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "leadingWhitespace":false,
+ "tbody":true,
+ "htmlSerialize":false,
+ "style":false,
+ "hrefNormalized":true,
+ "opacity":false,
+ "cssFloat":false,
+ "checkOn":true,
+ "optSelected":false,
+ "getSetAttribute":true,
+ "enctype":true,
+ "html5Clone":false,
+ "submitBubbles":false,
+ "changeBubbles":false,
+ "focusinBubbles":true,
+ "deleteExpando":false,
+ "noCloneEvent":false,
+ "inlineBlockNeedsLayout":false,
+ "shrinkWrapBlocks":false,
+ "reliableMarginRight":true,
+ "noCloneChecked":false,
+ "optDisabled":true,
+ "radioValue":false,
+ "checkClone":true,
+ "appendChecked":true,
+ "boxModel":true,
+ "reliableHiddenOffsets":false,
+ "ajax":true,
+ "cors":false,
+ "doesNotAddBorder":false,
+ "doesAddBorderForTableAndCells":true,
+ "fixedPosition":true,
+ "subtractsBorderForOverflowNotVisible":false,
+ "doesNotIncludeMarginInBodyOffset":true
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+} else if ( /msie 7\.0/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "ajax": true,
+ "appendChecked": false,
+ "boxModel": true,
+ "changeBubbles": false,
+ "checkClone": false,
+ "checkOn": true,
+ "cors": false,
+ "cssFloat": false,
+ "deleteExpando": false,
+ "doesAddBorderForTableAndCells": true,
+ "doesNotAddBorder": true,
+ "doesNotIncludeMarginInBodyOffset": true,
+ "enctype": true,
+ "fixedPosition": true,
+ "focusinBubbles": true,
+ "getSetAttribute": false,
+ "hrefNormalized": false,
+ "html5Clone": false,
+ "htmlSerialize": false,
+ "inlineBlockNeedsLayout": true,
+ "leadingWhitespace": false,
+ "noCloneChecked": false,
+ "noCloneEvent": false,
+ "opacity": false,
+ "optDisabled": true,
+ "optSelected": false,
+ "radioValue": false,
+ "reliableHiddenOffsets": false,
+ "reliableMarginRight": true,
+ "shrinkWrapBlocks": false,
+ "submitBubbles": false,
+ "subtractsBorderForOverflowNotVisible": false,
+ "tbody": false,
+ "style": false
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+} else if ( /msie 6\.0/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "leadingWhitespace":false,
+ "tbody":false,
+ "htmlSerialize":false,
+ "style":false,
+ "hrefNormalized":false,
+ "opacity":false,
+ "cssFloat":false,
+ "checkOn":true,
+ "optSelected":false,
+ "getSetAttribute":false,
+ "enctype":true,
+ "html5Clone":false,
+ "submitBubbles":false,
+ "changeBubbles":false,
+ "focusinBubbles":true,
+ "deleteExpando":false,
+ "noCloneEvent":false,
+ "inlineBlockNeedsLayout":true,
+ "shrinkWrapBlocks":true,
+ "reliableMarginRight":true,
+ "noCloneChecked":false,
+ "optDisabled":true,
+ "radioValue":false,
+ "checkClone":false,
+ "appendChecked":false,
+ "boxModel":true,
+ "reliableHiddenOffsets":false,
+ "ajax":true,
+ "cors":false,
+ "doesNotAddBorder":true,
+ "doesAddBorderForTableAndCells":true,
+ "fixedPosition":false,
+ "subtractsBorderForOverflowNotVisible":false,
+ "doesNotIncludeMarginInBodyOffset":true
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+} else if ( /5\.1\.1 safari/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "leadingWhitespace":true,
+ "tbody":true,
+ "htmlSerialize":true,
+ "style":true,
+ "hrefNormalized":true,
+ "opacity":true,
+ "cssFloat":true,
+ "checkOn":false,
+ "optSelected":true,
+ "getSetAttribute":true,
+ "enctype":true,
+ "html5Clone":true,
+ "submitBubbles":true,
+ "changeBubbles":true,
+ "focusinBubbles":false,
+ "deleteExpando":true,
+ "noCloneEvent":true,
+ "inlineBlockNeedsLayout":false,
+ "shrinkWrapBlocks":false,
+ "reliableMarginRight":true,
+ "noCloneChecked":true,
+ "optDisabled":true,
+ "radioValue":true,
+ "checkClone":true,
+ "appendChecked":true,
+ "boxModel":true,
+ "reliableHiddenOffsets":true,
+ "ajax":true,
+ "cors":true,
+ "doesNotAddBorder":true,
+ "doesAddBorderForTableAndCells":false,
+ "fixedPosition":true,
+ "subtractsBorderForOverflowNotVisible":false,
+ "doesNotIncludeMarginInBodyOffset":true
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+} else if ( /firefox\/3\.6/i.test(userAgent) ) {
+ test("Verify that the support tests resolve as expected per browser", function() {
+ var i,
+ expected = {
+ "leadingWhitespace":true,
+ "tbody":true,
+ "htmlSerialize":true,
+ "style":true,
+ "hrefNormalized":true,
+ "opacity":true,
+ "cssFloat":true,
+ "checkOn":true,
+ "optSelected":true,
+ "getSetAttribute":true,
+ "enctype":false,
+ "html5Clone":true,
+ "submitBubbles":true,
+ "changeBubbles":true,
+ "focusinBubbles":false,
+ "deleteExpando":true,
+ "noCloneEvent":true,
+ "inlineBlockNeedsLayout":false,
+ "shrinkWrapBlocks":false,
+ "reliableMarginRight":true,
+ "noCloneChecked":true,
+ "optDisabled":true,
+ "radioValue":true,
+ "checkClone":true,
+ "appendChecked":true,
+ "boxModel":true,
+ "reliableHiddenOffsets":true,
+ "ajax":true,
+ "cors":true,
+ "doesNotAddBorder":true,
+ "doesAddBorderForTableAndCells":true,
+ "fixedPosition":true,
+ "subtractsBorderForOverflowNotVisible":false,
+ "doesNotIncludeMarginInBodyOffset":true
+ };
+ for ( i in expected ) {
+ equal( jQuery.support[i], expected[i], "jQuery.support['" + i + "']: " + jQuery.support[i] + ", expected['" + i + "']: " + expected[i]);
+ }
+ });
+}

18 comments on commit 3d6237e

@rwaldron
jQuery Foundation member

These tests are great.

@rwaldron
jQuery Foundation member

One thing... really inconsistent with space around foo("here") vs foo( "here" )...

@timmywil
jQuery Foundation member

True, that is inconsistent. I should fix that.

But...since we're discussing it, I prefer foo("here") to foo( "here" ). Double quotes provide a visible space by themselves don't you think?

@rwaldron
jQuery Foundation member

@timmywil As it turns out, I do agree. My nit wasn't that it should be padded, but just that you're all over the damn place ;)

I think I'm going to open a ticket to officially change the style guide to reflect this, what do you think? I'll deal with the grunt work myself.

@rdworth

@rwldrn your proposed change seems like a real source for even worse inconsistency. Imagine
foo("here", 4 )
or
foo( 4, "here")
Yuck.

@rwaldron
jQuery Foundation member

@rdworth tough one, part of me thinks that's not too bad and pretty damn readable.

@rdworth

My cry is not "it's not readable because of lack of horizontal space" it's "that's an ugly inconsistency that adds complexity to something that should be as simple as possible (and already isn't)". My 2c

@rwaldron
jQuery Foundation member

@rdworth hahaha, ok! stop yelling at me.. geez... ;)

@rwaldron
jQuery Foundation member

@timmywil Can you update the whitespace in accordance with style guide - thanks dude :)

@timmywil
jQuery Foundation member

haha, and it's over that quick? @rdworth: we already have an answer to that. If either one or both of the parenthesis do not have quotes next to them, insert an extra space next to the quote. So what I propose would still be:

foo( "here", 4 );
foo( 4, "here" );
foo("here");
@rdworth

Oh, if it's single arg only, I won't lose sleep. Sorry for the noise.

@rwaldron
jQuery Foundation member

Yes, that is nice. Thanks for the follow through haha.

@eddiemonge

How is foo(4) handled? is it like that or foo( 4 )? If its the second, then seems like it should always be spaced wrapped in parens

@timmywil
jQuery Foundation member

Everything else is the same, so foo( 4 ). Quotes on both sides of the parens is the only exception.

@mikesherov
jQuery Foundation member
@rdworth

Quotes on both sides of the parens is the only exception.

Other than curly braces and square brackets which are the current exceptions it is proposed quotes be added to ;)

@eddiemonge

So many inconsistencies. I say its spaces next to the parens always, and maybe braces and brackets should be added to that.

@rwaldron
jQuery Foundation member

Other style guide changes are not up for discussion. Thanks to everyone, please consider the thread closed

Please sign in to comment.