Skip to content

Commit 9f9137a

Browse files
authored
fix(modal,flyout): autofocus fix for input and non focusable elements
Autofocus was not taking care of invisible inputs (in dropdown) classname observer was too sensible and accidently triggered autofocus (search, dropdown, form validation) autofocus should only focus inputs, otherwise focusing the module directly for focustrap click into an input when window was refocused triggered autofocus instead of respecting the clicked input
1 parent 2806406 commit 9f9137a

File tree

2 files changed

+47
-38
lines changed

2 files changed

+47
-38
lines changed

src/definitions/modules/flyout.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@
8686
initialBodyMargin = '',
8787
tempBodyMargin = '',
8888
hadScrollbar = false,
89+
windowRefocused = false,
8990

9091
elementNamespace,
9192
id,
@@ -264,9 +265,13 @@
264265
module.setup.heights();
265266
},
266267
focus: function () {
267-
if (module.is.visible() && settings.autofocus && settings.dimPage) {
268+
windowRefocused = true;
269+
},
270+
click: function (event) {
271+
if (windowRefocused && document.activeElement !== event.target && module.is.visible() && settings.autofocus && settings.dimPage && $(document.activeElement).closest(selector.flyout).length === 0) {
268272
requestAnimationFrame(module.set.autofocus);
269273
}
274+
windowRefocused = false;
270275
},
271276
clickaway: function (event) {
272277
if (settings.closable) {
@@ -373,6 +378,9 @@
373378
$window
374379
.on('focus' + elementNamespace, module.event.focus)
375380
;
381+
$context
382+
.on('click' + elementNamespace, module.event.click)
383+
;
376384
},
377385
clickaway: function () {
378386
module.verbose('Adding clickaway events to context', $context);
@@ -502,11 +510,12 @@
502510

503511
return nodes;
504512
},
505-
shouldRefreshInputs = false
513+
shouldRefreshInputs = false,
514+
ignoreAutofocus = true
506515
;
507516
mutations.every(function (mutation) {
508517
if (mutation.type === 'attributes') {
509-
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').length > 0)) {
518+
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
510519
shouldRefreshInputs = true;
511520
}
512521
} else {
@@ -516,14 +525,15 @@
516525
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
517526
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
518527
shouldRefreshInputs = true;
528+
ignoreAutofocus = false;
519529
}
520530
}
521531

522532
return !shouldRefreshInputs;
523533
});
524534

525535
if (shouldRefreshInputs) {
526-
module.refreshInputs();
536+
module.refreshInputs(ignoreAutofocus);
527537
}
528538
});
529539
observer.observe(element, {
@@ -548,7 +558,7 @@
548558
$flyouts = $context.children(selector.flyout);
549559
},
550560

551-
refreshInputs: function () {
561+
refreshInputs: function (ignoreAutofocus) {
552562
if ($inputs) {
553563
$inputs
554564
.off('keydown' + elementNamespace)
@@ -560,8 +570,8 @@
560570
$inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
561571
return $(this).closest('.disabled').length === 0;
562572
});
563-
if ($inputs.length === 0) {
564-
$inputs = $module;
573+
if ($inputs.filter(':input').length === 0) {
574+
$inputs = $module.add($inputs);
565575
$module.attr('tabindex', -1);
566576
} else {
567577
$module.removeAttr('tabindex');
@@ -572,7 +582,7 @@
572582
$inputs.last()
573583
.on('keydown' + elementNamespace, module.event.inputKeyDown.last)
574584
;
575-
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
585+
if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
576586
module.set.autofocus();
577587
}
578588
},
@@ -850,20 +860,14 @@
850860
var
851861
$autofocus = $inputs.filter('[autofocus]'),
852862
$rawInputs = $inputs.filter(':input'),
853-
$input = $autofocus.length > 0
854-
? $autofocus.first()
863+
$input = ($autofocus.length > 0
864+
? $autofocus
855865
: ($rawInputs.length > 0
856866
? $rawInputs
857-
: $inputs.filter(':not(i.close)')
858-
).first()
867+
: $module)
868+
).first()
859869
;
860-
// check if only the close icon is remaining
861-
if ($input.length === 0 && $inputs.length > 0) {
862-
$input = $inputs.first();
863-
}
864-
if ($input.length > 0) {
865-
$input.trigger('focus');
866-
}
870+
$input.trigger('focus');
867871
},
868872
dimmerStyles: function () {
869873
if (settings.blurring) {

src/definitions/modules/modal.js

Lines changed: 24 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@
8888
tempBodyMargin = '',
8989
keepScrollingClass = false,
9090
hadScrollbar = false,
91+
windowRefocused = false,
9192

9293
elementEventNamespace,
9394
id,
@@ -251,6 +252,7 @@
251252
.off(eventNamespace)
252253
;
253254
$window.off(elementEventNamespace);
255+
$context.off(elementEventNamespace);
254256
$dimmer.off(elementEventNamespace);
255257
$closeIcon.off(elementEventNamespace);
256258
if ($inputs) {
@@ -272,11 +274,12 @@
272274
return nodes;
273275
},
274276
shouldRefresh = false,
275-
shouldRefreshInputs = false
277+
shouldRefreshInputs = false,
278+
ignoreAutofocus = true
276279
;
277280
mutations.every(function (mutation) {
278281
if (mutation.type === 'attributes') {
279-
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').length > 0)) {
282+
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
280283
shouldRefreshInputs = true;
281284
}
282285
} else {
@@ -287,6 +290,7 @@
287290
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
288291
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
289292
shouldRefreshInputs = true;
293+
ignoreAutofocus = false;
290294
}
291295
}
292296

@@ -298,7 +302,7 @@
298302
module.refresh();
299303
}
300304
if (shouldRefreshInputs) {
301-
module.refreshInputs();
305+
module.refreshInputs(ignoreAutofocus);
302306
}
303307
});
304308
observer.observe(element, {
@@ -326,7 +330,7 @@
326330
$allModals = $otherModals.add($module);
327331
},
328332

329-
refreshInputs: function () {
333+
refreshInputs: function (ignoreAutofocus) {
330334
if ($inputs) {
331335
$inputs
332336
.off('keydown' + elementEventNamespace)
@@ -335,8 +339,8 @@
335339
$inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
336340
return $(this).closest('.disabled').length === 0;
337341
});
338-
if ($inputs.length === 0) {
339-
$inputs = $module;
342+
if ($inputs.filter(':input').length === 0) {
343+
$inputs = $module.add($inputs);
340344
$module.attr('tabindex', -1);
341345
} else {
342346
$module.removeAttr('tabindex');
@@ -347,7 +351,7 @@
347351
$inputs.last()
348352
.on('keydown' + elementEventNamespace, module.event.inputKeyDown.last)
349353
;
350-
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
354+
if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
351355
module.set.autofocus();
352356
}
353357
},
@@ -385,6 +389,9 @@
385389
.on('resize' + elementEventNamespace, module.event.resize)
386390
.on('focus' + elementEventNamespace, module.event.focus)
387391
;
392+
$context
393+
.on('click' + elementEventNamespace, module.event.click)
394+
;
388395
},
389396
scrollLock: function () {
390397
// touch events default to passive, due to changes in chrome to optimize mobile perf
@@ -542,9 +549,13 @@
542549
}
543550
},
544551
focus: function () {
545-
if ($dimmable.dimmer('is active') && module.is.active() && settings.autofocus) {
552+
windowRefocused = true;
553+
},
554+
click: function (event) {
555+
if (windowRefocused && document.activeElement !== event.target && $dimmable.dimmer('is active') && module.is.active() && settings.autofocus && $(document.activeElement).closest(selector.modal).length === 0) {
546556
requestAnimationFrame(module.set.autofocus);
547557
}
558+
windowRefocused = false;
548559
},
549560
},
550561

@@ -1054,20 +1065,14 @@
10541065
var
10551066
$autofocus = $inputs.filter('[autofocus]'),
10561067
$rawInputs = $inputs.filter(':input'),
1057-
$input = $autofocus.length > 0
1058-
? $autofocus.first()
1068+
$input = ($autofocus.length > 0
1069+
? $autofocus
10591070
: ($rawInputs.length > 0
10601071
? $rawInputs
1061-
: $inputs.filter(':not(i.close)')
1062-
).first()
1072+
: $module)
1073+
).first()
10631074
;
1064-
// check if only the close icon is remaining
1065-
if ($input.length === 0 && $inputs.length > 0) {
1066-
$input = $inputs.first();
1067-
}
1068-
if ($input.length > 0) {
1069-
$input.trigger('focus');
1070-
}
1075+
$input.trigger('focus');
10711076
},
10721077
bodyMargin: function () {
10731078
var position = module.can.leftBodyScrollbar() ? 'left' : 'right';

0 commit comments

Comments
 (0)