Skip to content

Commit c8c6ab6

Browse files
markelogdmethvin
authored andcommitted
Fix #12569. Improve feature detect for event bubbling. Close jquerygh-1076.
1 parent 00bbbe2 commit c8c6ab6

File tree

4 files changed

+180
-86
lines changed

4 files changed

+180
-86
lines changed

src/support.js

Lines changed: 38 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,6 @@
11
jQuery.support = (function() {
22

3-
var support,
4-
all,
5-
a,
6-
select,
7-
opt,
8-
input,
9-
fragment,
10-
eventName,
11-
i,
12-
isSupported,
13-
clickFn,
3+
var support, all, a, select, opt, input, fragment, eventName, isSupported, i,
144
div = document.createElement("div");
155

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

8272
// Will be defined later
83-
submitBubbles: true,
84-
changeBubbles: true,
85-
focusinBubbles: false,
8673
deleteExpando: true,
8774
noCloneEvent: true,
8875
inlineBlockNeedsLayout: false,
@@ -101,24 +88,13 @@ jQuery.support = (function() {
10188
select.disabled = true;
10289
support.optDisabled = !opt.disabled;
10390

104-
// Test to see if it's possible to delete an expando from an element
105-
// Fails in Internet Explorer
91+
// Support: IE<9
10692
try {
10793
delete div.test;
10894
} catch( e ) {
10995
support.deleteExpando = false;
11096
}
11197

112-
if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
113-
div.attachEvent( "onclick", clickFn = function() {
114-
// Cloning a node shouldn't copy over any
115-
// bound event handlers (IE does this)
116-
support.noCloneEvent = false;
117-
});
118-
div.cloneNode( true ).fireEvent("onclick");
119-
div.detachEvent( "onclick", clickFn );
120-
}
121-
12298
// Check if we can trust getAttribute("value")
12399
input = document.createElement("input");
124100
input.setAttribute( "value", "" );
@@ -130,49 +106,42 @@ jQuery.support = (function() {
130106
support.radioValue = input.value === "t";
131107

132108
// #11217 - WebKit loses check when the name is after the checked attribute
133-
input.setAttribute( "checked", "checked" );
109+
input.setAttribute( "checked", "t" );
134110
input.setAttribute( "name", "t" );
135111

136-
div.appendChild( input );
137112
fragment = document.createDocumentFragment();
138-
fragment.appendChild( div.lastChild );
139-
140-
// WebKit doesn't clone checked state correctly in fragments
141-
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
113+
fragment.appendChild( input );
142114

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

147-
fragment.removeChild( input );
148-
fragment.appendChild( div );
119+
// WebKit doesn't clone checked state correctly in fragments
120+
support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
149121

150-
// Technique from Juriy Zaytsev
151-
// http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
152-
// We only care about the case where non-standard event systems
153-
// are used, namely in IE. Short-circuiting here helps us to
154-
// avoid an eval call (in setAttribute) which can cause CSP
155-
// to go haywire. See: https://developer.mozilla.org/en/Security/CSP
122+
// Support: IE<9
123+
// Opera does not clone events (and typeof div.attachEvent === undefined).
124+
// IE9-10 clones events bound via attachEvent, but they don't trigger with .click()
156125
if ( div.attachEvent ) {
157-
for ( i in {
158-
submit: true,
159-
change: true,
160-
focusin: true
161-
}) {
162-
eventName = "on" + i;
163-
isSupported = ( eventName in div );
164-
if ( !isSupported ) {
165-
div.setAttribute( eventName, "return;" );
166-
isSupported = ( typeof div[ eventName ] === "function" );
167-
}
168-
support[ i + "Bubbles" ] = isSupported;
169-
}
126+
div.attachEvent( "onclick", function() {
127+
support.noCloneEvent = false;
128+
});
129+
130+
div.cloneNode( true ).click();
131+
}
132+
133+
// Support: IE<9 (lack submit/change bubble), Firefox 17+ (lack focusin event)
134+
// Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP), test/csp.php
135+
for ( i in { submit: true, change: true, focusin: true }) {
136+
div.setAttribute( eventName = "on" + i, "t" );
137+
138+
support[ i + "Bubbles" ] = eventName in window || div.attributes[ eventName ].expando === false;
170139
}
171140

172141
// Run tests that need a body at doc ready
173142
jQuery(function() {
174-
var container, div, tds, marginDiv,
175-
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;",
143+
var container, marginDiv, tds,
144+
divReset = "padding:0;margin:0;border:0;display:block;box-sizing:content-box;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;",
176145
body = document.getElementsByTagName("body")[0];
177146

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

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

187-
// Construct the test element
188-
div = document.createElement("div");
189-
container.appendChild( div );
155+
body.appendChild( container ).appendChild( div );
190156

157+
// Support: IE8
191158
// Check if table cells still have offsetWidth/Height when they are set
192159
// to display:none and there are still other visible table cells in a
193160
// table row; if so, offsetWidth/Height are not reliable for use when
194161
// determining if an element has been hidden directly using
195162
// display:none (it is still safe to use offsets if a parent element is
196163
// hidden; don safety goggles and see bug #4512 for more information).
197-
// (only IE 8 fails this test)
198164
div.innerHTML = "<table><tr><td></td><td>t</td></tr></table>";
199165
tds = div.getElementsByTagName("td");
200166
tds[ 0 ].style.cssText = "padding:0;margin:0;border:0;display:none";
@@ -203,8 +169,8 @@ jQuery.support = (function() {
203169
tds[ 0 ].style.display = "";
204170
tds[ 1 ].style.display = "none";
205171

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

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

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

222187
// Check if div with explicit width and no margin-right incorrectly
223-
// gets computed margin-right based on width of container. For more
224-
// info see bug #3333
188+
// gets computed margin-right based on width of container. (#3333)
225189
// Fails in WebKit before Feb 2011 nightlies
226190
// WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
227-
marginDiv = document.createElement("div");
191+
marginDiv = div.appendChild( document.createElement("div") );
228192
marginDiv.style.cssText = div.style.cssText = divReset;
229193
marginDiv.style.marginRight = marginDiv.style.width = "0";
230194
div.style.width = "1px";
231-
div.appendChild( marginDiv );
195+
232196
support.reliableMarginRight =
233197
!parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight );
234198
}
235199

236200
if ( typeof div.style.zoom !== "undefined" ) {
201+
// Support: IE<8
237202
// Check if natively block-level elements act like inline-block
238203
// elements when setting their display to 'inline' and giving
239204
// them layout
240-
// (IE < 8 does this)
241205
div.innerHTML = "";
242206
div.style.cssText = divReset + "width:1px;padding:1px;display:inline;zoom:1";
243207
support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
244208

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

258-
// Null elements to avoid leaks in IE
259221
body.removeChild( container );
222+
223+
// Null elements to avoid leaks in IE
260224
container = div = tds = marginDiv = null;
261225
});
262226

263227
// Null elements to avoid leaks in IE
264-
fragment.removeChild( div );
265-
all = a = select = opt = input = fragment = div = null;
228+
all = select = fragment = opt = a = input = null;
266229

267230
return support;
268231
})();
232+

test/csp.php

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
<?php header("X-Content-Security-Policy-Report-Only: allow *"); ?>
1+
<?php
2+
header("X-Content-Security-Policy: default-src localhost 'self';");
3+
header("X-WebKit-CSP: script-src 'self'; style-src 'self' 'unsafe-inline'");
4+
?>
25
<!DOCTYPE html>
36
<html>
47
<head>

test/unit/event.js

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2601,3 +2601,31 @@ test( "make sure events cloned correctly", 18, function() {
26012601
clone.find("p:first").click(); // 0 should be fired
26022602
clone.find("#check1").change(); // 0 events should fire
26032603
});
2604+
2605+
test( "Check order of focusin/focusout events", 2, function() {
2606+
var focus, blur,
2607+
input = jQuery("#name");
2608+
2609+
input.on("focus", function() {
2610+
focus = true;
2611+
2612+
}).on("focusin", function() {
2613+
ok( !focus, "Focusin event should fire before focus does" );
2614+
2615+
}).on("blur", function() {
2616+
blur = true;
2617+
2618+
}).on("focusout", function() {
2619+
ok( !blur, "Focusout event should fire before blur does" );
2620+
});
2621+
2622+
// gain focus
2623+
input.focus();
2624+
2625+
// then lose it
2626+
jQuery("#search").focus();
2627+
2628+
// cleanup
2629+
input.off();
2630+
});
2631+

0 commit comments

Comments
 (0)