Skip to content

Commit

Permalink
fix(modal,flyout): autofocus fix for input and non focusable elements
Browse files Browse the repository at this point in the history
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
  • Loading branch information
lubber-de committed Feb 26, 2023
1 parent 2806406 commit 9f9137a
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 38 deletions.
42 changes: 23 additions & 19 deletions src/definitions/modules/flyout.js
Expand Up @@ -86,6 +86,7 @@
initialBodyMargin = '',
tempBodyMargin = '',
hadScrollbar = false,
windowRefocused = false,

elementNamespace,
id,
Expand Down Expand Up @@ -264,9 +265,13 @@
module.setup.heights();
},
focus: function () {
if (module.is.visible() && settings.autofocus && settings.dimPage) {
windowRefocused = true;
},
click: function (event) {
if (windowRefocused && document.activeElement !== event.target && module.is.visible() && settings.autofocus && settings.dimPage && $(document.activeElement).closest(selector.flyout).length === 0) {
requestAnimationFrame(module.set.autofocus);
}
windowRefocused = false;
},
clickaway: function (event) {
if (settings.closable) {
Expand Down Expand Up @@ -373,6 +378,9 @@
$window
.on('focus' + elementNamespace, module.event.focus)
;
$context
.on('click' + elementNamespace, module.event.click)
;
},
clickaway: function () {
module.verbose('Adding clickaway events to context', $context);
Expand Down Expand Up @@ -502,11 +510,12 @@

return nodes;
},
shouldRefreshInputs = false
shouldRefreshInputs = false,
ignoreAutofocus = true
;
mutations.every(function (mutation) {
if (mutation.type === 'attributes') {
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').length > 0)) {
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
shouldRefreshInputs = true;
}
} else {
Expand All @@ -516,14 +525,15 @@
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
shouldRefreshInputs = true;
ignoreAutofocus = false;
}
}

return !shouldRefreshInputs;
});

if (shouldRefreshInputs) {
module.refreshInputs();
module.refreshInputs(ignoreAutofocus);
}
});
observer.observe(element, {
Expand All @@ -548,7 +558,7 @@
$flyouts = $context.children(selector.flyout);
},

refreshInputs: function () {
refreshInputs: function (ignoreAutofocus) {
if ($inputs) {
$inputs
.off('keydown' + elementNamespace)
Expand All @@ -560,8 +570,8 @@
$inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
return $(this).closest('.disabled').length === 0;
});
if ($inputs.length === 0) {
$inputs = $module;
if ($inputs.filter(':input').length === 0) {
$inputs = $module.add($inputs);
$module.attr('tabindex', -1);
} else {
$module.removeAttr('tabindex');
Expand All @@ -572,7 +582,7 @@
$inputs.last()
.on('keydown' + elementNamespace, module.event.inputKeyDown.last)
;
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
module.set.autofocus();
}
},
Expand Down Expand Up @@ -850,20 +860,14 @@
var
$autofocus = $inputs.filter('[autofocus]'),
$rawInputs = $inputs.filter(':input'),
$input = $autofocus.length > 0
? $autofocus.first()
$input = ($autofocus.length > 0
? $autofocus
: ($rawInputs.length > 0
? $rawInputs
: $inputs.filter(':not(i.close)')
).first()
: $module)
).first()
;
// check if only the close icon is remaining
if ($input.length === 0 && $inputs.length > 0) {
$input = $inputs.first();
}
if ($input.length > 0) {
$input.trigger('focus');
}
$input.trigger('focus');
},
dimmerStyles: function () {
if (settings.blurring) {
Expand Down
43 changes: 24 additions & 19 deletions src/definitions/modules/modal.js
Expand Up @@ -88,6 +88,7 @@
tempBodyMargin = '',
keepScrollingClass = false,
hadScrollbar = false,
windowRefocused = false,

elementEventNamespace,
id,
Expand Down Expand Up @@ -251,6 +252,7 @@
.off(eventNamespace)
;
$window.off(elementEventNamespace);
$context.off(elementEventNamespace);
$dimmer.off(elementEventNamespace);
$closeIcon.off(elementEventNamespace);
if ($inputs) {
Expand All @@ -272,11 +274,12 @@
return nodes;
},
shouldRefresh = false,
shouldRefreshInputs = false
shouldRefreshInputs = false,
ignoreAutofocus = true
;
mutations.every(function (mutation) {
if (mutation.type === 'attributes') {
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').length > 0)) {
if (observeAttributes && (mutation.attributeName === 'disabled' || $(mutation.target).find(':input').addBack(':input').filter(':visible').length > 0)) {
shouldRefreshInputs = true;
}
} else {
Expand All @@ -287,6 +290,7 @@
$removedInputs = $(collectNodes(mutation.removedNodes)).filter('a[href], [tabindex], :input');
if ($addedInputs.length > 0 || $removedInputs.length > 0) {
shouldRefreshInputs = true;
ignoreAutofocus = false;
}
}

Expand All @@ -298,7 +302,7 @@
module.refresh();
}
if (shouldRefreshInputs) {
module.refreshInputs();
module.refreshInputs(ignoreAutofocus);
}
});
observer.observe(element, {
Expand Down Expand Up @@ -326,7 +330,7 @@
$allModals = $otherModals.add($module);
},

refreshInputs: function () {
refreshInputs: function (ignoreAutofocus) {
if ($inputs) {
$inputs
.off('keydown' + elementEventNamespace)
Expand All @@ -335,8 +339,8 @@
$inputs = $module.find('a[href], [tabindex], :input:enabled').filter(':visible').filter(function () {
return $(this).closest('.disabled').length === 0;
});
if ($inputs.length === 0) {
$inputs = $module;
if ($inputs.filter(':input').length === 0) {
$inputs = $module.add($inputs);
$module.attr('tabindex', -1);
} else {
$module.removeAttr('tabindex');
Expand All @@ -347,7 +351,7 @@
$inputs.last()
.on('keydown' + elementEventNamespace, module.event.inputKeyDown.last)
;
if (settings.autofocus && $inputs.filter(':focus').length === 0) {
if (!ignoreAutofocus && settings.autofocus && $inputs.filter(':focus').length === 0) {
module.set.autofocus();
}
},
Expand Down Expand Up @@ -385,6 +389,9 @@
.on('resize' + elementEventNamespace, module.event.resize)
.on('focus' + elementEventNamespace, module.event.focus)
;
$context
.on('click' + elementEventNamespace, module.event.click)
;
},
scrollLock: function () {
// touch events default to passive, due to changes in chrome to optimize mobile perf
Expand Down Expand Up @@ -542,9 +549,13 @@
}
},
focus: function () {
if ($dimmable.dimmer('is active') && module.is.active() && settings.autofocus) {
windowRefocused = true;
},
click: function (event) {
if (windowRefocused && document.activeElement !== event.target && $dimmable.dimmer('is active') && module.is.active() && settings.autofocus && $(document.activeElement).closest(selector.modal).length === 0) {
requestAnimationFrame(module.set.autofocus);
}
windowRefocused = false;
},
},

Expand Down Expand Up @@ -1054,20 +1065,14 @@
var
$autofocus = $inputs.filter('[autofocus]'),
$rawInputs = $inputs.filter(':input'),
$input = $autofocus.length > 0
? $autofocus.first()
$input = ($autofocus.length > 0
? $autofocus
: ($rawInputs.length > 0
? $rawInputs
: $inputs.filter(':not(i.close)')
).first()
: $module)
).first()
;
// check if only the close icon is remaining
if ($input.length === 0 && $inputs.length > 0) {
$input = $inputs.first();
}
if ($input.length > 0) {
$input.trigger('focus');
}
$input.trigger('focus');
},
bodyMargin: function () {
var position = module.can.leftBodyScrollbar() ? 'left' : 'right';
Expand Down

0 comments on commit 9f9137a

Please sign in to comment.