Skip to content

Commit

Permalink
Fix #12569. Improve feature detect for event bubbling. Close gh-1076.
Browse files Browse the repository at this point in the history
  • Loading branch information
markelog authored and dmethvin committed Dec 21, 2012
1 parent 00bbbe2 commit c8c6ab6
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 86 deletions.
112 changes: 38 additions & 74 deletions src/support.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,6 @@
jQuery.support = (function() {

var support,
all,
a,
select,
opt,
input,
fragment,
eventName,
i,
isSupported,
clickFn,
var support, all, a, select, opt, input, fragment, eventName, isSupported, i,
div = document.createElement("div");

// Setup
Expand Down Expand Up @@ -80,9 +70,6 @@ jQuery.support = (function() {
boxModel: document.compatMode === "CSS1Compat",

// Will be defined later
submitBubbles: true,
changeBubbles: true,
focusinBubbles: false,
deleteExpando: true,
noCloneEvent: true,
inlineBlockNeedsLayout: false,
Expand All @@ -101,24 +88,13 @@ jQuery.support = (function() {
select.disabled = true;
support.optDisabled = !opt.disabled;

// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
// Support: IE<9
try {
delete div.test;
} catch( e ) {
support.deleteExpando = false;
}

if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
div.attachEvent( "onclick", clickFn = function() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
support.noCloneEvent = false;
});
div.cloneNode( true ).fireEvent("onclick");
div.detachEvent( "onclick", clickFn );
}

// Check if we can trust getAttribute("value")
input = document.createElement("input");
input.setAttribute( "value", "" );
Expand All @@ -130,49 +106,42 @@ jQuery.support = (function() {
support.radioValue = input.value === "t";

// #11217 - WebKit loses check when the name is after the checked attribute
input.setAttribute( "checked", "checked" );
input.setAttribute( "checked", "t" );
input.setAttribute( "name", "t" );

div.appendChild( input );
fragment = document.createDocumentFragment();
fragment.appendChild( div.lastChild );

// WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
fragment.appendChild( input );

// Check if a disconnected checkbox will retain its checked
// value of true after appended to the DOM (IE6/7)
support.appendChecked = input.checked;

fragment.removeChild( input );
fragment.appendChild( div );
// WebKit doesn't clone checked state correctly in fragments
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;

// Technique from Juriy Zaytsev
// http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
// We only care about the case where non-standard event systems
// are used, namely in IE. Short-circuiting here helps us to
// avoid an eval call (in setAttribute) which can cause CSP
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
// Support: IE<9
// Opera does not clone events (and typeof div.attachEvent === undefined).
// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
if ( div.attachEvent ) {
for ( i in {
submit: true,
change: true,
focusin: true
}) {
eventName = "on" + i;
isSupported = ( eventName in div );
if ( !isSupported ) {
div.setAttribute( eventName, "return;" );
isSupported = ( typeof div[ eventName ] === "function" );
}
support[ i + "Bubbles" ] = isSupported;
}
div.attachEvent( "onclick", function() {
support.noCloneEvent = false;
});

div.cloneNode( true ).click();
}

// Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
for ( i in { submit: true, change: true, focusin: true }) {
div.setAttribute( eventName = "on" + i, "t" );

support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
}

// Run tests that need a body at doc ready
jQuery(function() {
var container, div, tds, marginDiv,
divReset = "padding:0;margin:0;border:0;display:block;overflow:hidden;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
var container, marginDiv, tds,
divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
body = document.getElementsByTagName("body")[0];

if ( !body ) {
Expand All @@ -181,20 +150,17 @@ jQuery.support = (function() {
}

container = document.createElement("div");
container.style.cssText = "visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px";
body.insertBefore( container, body.firstChild );
container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px";

// Construct the test element
div = document.createElement("div");
container.appendChild( div );
body.appendChild( container ).appendChild( div );

// Support: IE8
// 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></td><td>t</td></tr></table>";
tds = div.getElementsByTagName("td");
tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
Expand All @@ -203,8 +169,8 @@ jQuery.support = (function() {
tds[ 0 ].style.display = "";
tds[ 1 ].style.display = "none";

// Support: IE8
// Check if empty table cells still have offsetWidth/Height
// (IE <= 8 fail this test)
support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );

// Check box-sizing and margin behavior
Expand All @@ -213,39 +179,36 @@ jQuery.support = (function() {
support.boxSizing = ( div.offsetWidth === 4 );
support.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== 1 );

// NOTE: To any future maintainer, we've window.getComputedStyle
// because jsdom on node.js will break without it.
// Use window.getComputedStyle because jsdom on node.js will break without it.
if ( window.getComputedStyle ) {
support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%";
support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px";

// 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
// gets computed margin-right based on width of container. (#3333)
// Fails in WebKit before Feb 2011 nightlies
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
marginDiv = document.createElement("div");
marginDiv = div.appendChild( document.createElement("div") );
marginDiv.style.cssText = div.style.cssText = divReset;
marginDiv.style.marginRight = marginDiv.style.width = "0";
div.style.width = "1px";
div.appendChild( marginDiv );

support.reliableMarginRight =
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
}

if ( typeof div.style.zoom !== "undefined" ) {
// Support: IE<8
// 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.innerHTML = "";
div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );

// Support: IE6
// Check if elements with layout shrink-wrap their children
// (IE 6 does this)
div.style.display = "block";
div.style.overflow = "visible";
div.innerHTML = "<div></div>";
div.firstChild.style.width = "5px";
support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
Expand All @@ -255,14 +218,15 @@ jQuery.support = (function() {
body.style.zoom = 1;
}

// Null elements to avoid leaks in IE
body.removeChild( container );

// Null elements to avoid leaks in IE
container = div = tds = marginDiv = null;
});

// Null elements to avoid leaks in IE
fragment.removeChild( div );
all = a = select = opt = input = fragment = div = null;
all = select = fragment = opt = a = input = null;

return support;
})();

5 changes: 4 additions & 1 deletion test/csp.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
<?php header("X-Content-Security-Policy-Report-Only: allow *"); ?>
<?php
header("X-Content-Security-Policy: default-src localhost 'self';");
header("X-WebKit-CSP: script-src 'self'; style-src 'self' 'unsafe-inline'");
?>
<!DOCTYPE html>
<html>
<head>
Expand Down
28 changes: 28 additions & 0 deletions test/unit/event.js
Original file line number Diff line number Diff line change
Expand Up @@ -2601,3 +2601,31 @@ test( "make sure events cloned correctly", 18, function() {
clone.find("p:first").click(); // 0 should be fired
clone.find("#check1").change(); // 0 events should fire
});

test( "Check order of focusin/focusout events", 2, function() {
var focus, blur,
input = jQuery("#name");

input.on("focus", function() {
focus = true;

}).on("focusin", function() {
ok( !focus, "Focusin event should fire before focus does" );

}).on("blur", function() {
blur = true;

}).on("focusout", function() {
ok( !blur, "Focusout event should fire before blur does" );
});

// gain focus
input.focus();

// then lose it
jQuery("#search").focus();

// cleanup
input.off();
});

Loading

0 comments on commit c8c6ab6

Please sign in to comment.