diff --git a/CHANGES.md b/CHANGES.md index 5f2fa55bf..a96b598c0 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -48,6 +48,7 @@ - pat date picker: Support updating a date if it is before another dependent date. - pat tabs: Refactor based on ``ResizeObserver`` and fix problems calculating the with with transitions. - pat tabs: When clicking on the ``extra-tabs`` element, toggle between ``open`` and ``closed`` classes to allow opening/closing an extra-tabs menu via CSS. +- pat autofocus: Do not autofocus in iframes. Fixes: #761. ### Technical @@ -82,6 +83,9 @@ - pat scroll: Fix scrolling offset incorrectly applied. Fixes: #763. - Core registry: Fix ``transformPattern`` to also work with patterns which extend from Base. Fixes a problem with pat-auto-suggest not auto submitting. +- pat autofocus: Implement documented behavior to not focus on prefilled element, if there is another autofocus element which is empty. +- pat autofocus: Instead of calling autofocus for each element call it only once. +- pat autofocus: Register event handler only once. ## 3.0.0-dev - unreleased diff --git a/src/pat/autofocus/autofocus.js b/src/pat/autofocus/autofocus.js index e36582e10..a82a6118d 100644 --- a/src/pat/autofocus/autofocus.js +++ b/src/pat/autofocus/autofocus.js @@ -1,34 +1,44 @@ -/** - * Patterns autofocus - enhanced autofocus form elements - * - * Copyright 2012-2013 Simplon B.V. - Wichert Akkerman - */ import $ from "jquery"; -import registry from "../../core/registry"; +import Base from "../../core/base"; -var autofocus = { +let scheduled_task = null; +let registered_event_handler = false; + +export default Base.extend({ name: "autofocus", trigger: ":input.pat-autofocus,:input[autofocus]", - init: function init() { + init() { + if (window.self !== window.top) { + // Do not autofocus in iframes. + return; + } + this.setFocus(this.trigger); - $(document).on("patterns-injected", function (e) { - autofocus.setFocus($(e.target).find(autofocus.trigger)); - }); - $(document).on("pat-update", function (e) { - autofocus.setFocus($(e.target).find(autofocus.trigger)); - }); - }, - setFocus: function (target) { - var $all = $(target); - var $visible = $all.filter(function () { - if ($(this).is(":visible")) return true; - }); - setTimeout(function () { - $visible.get(0) && $visible.get(0).focus(); - }, 10); + + if (!registered_event_handler) { + // Register the event handler only once. + $(document).on("patterns-injected pat-update", (e) => { + this.setFocus($(e.target).find(this.trigger)); + }); + registered_event_handler = true; + } }, -}; -registry.register(autofocus); -export default autofocus; + setFocus(target) { + // Exit if task is scheduled. setFocus operates on whole DOM anyways. + if (scheduled_task) { + return; + } + const $all = $(target); + const visible = [...$all].filter((it) => $(it).is(":visible")); + const empty = visible.filter((it) => it.value === ""); + const el = empty[0] || visible[0]; + if (el) { + scheduled_task = setTimeout(() => { + el.focus(); + scheduled_task = null; + }, 10); + } + }, +}); diff --git a/src/pat/autofocus/autofocus.test.js b/src/pat/autofocus/autofocus.test.js new file mode 100644 index 000000000..8a510c441 --- /dev/null +++ b/src/pat/autofocus/autofocus.test.js @@ -0,0 +1,46 @@ +import pattern from "./autofocus"; +import utils from "../../core/utils"; + +describe("pat-autofocus", function () { + beforeEach(function () { + const el = document.createElement("div"); + el.setAttribute("id", "lab"); + document.body.append(el); + }); + + afterEach(function () { + document.body.innerHTML = ""; + }); + + it("Focus the first element.", async (done) => { + const container = document.querySelector("#lab"); + container.innerHTML = ` + + + + `; + pattern.init(container); + await utils.timeout(20); + + const should_be_active = document.querySelector("input[name=i1]"); + expect(document.activeElement).toBe(should_be_active); + + done(); + }); + + it("Focus the non-empty element, if available.", async (done) => { + const container = document.querySelector("#lab"); + container.innerHTML = ` + + + + `; + pattern.init(container); + await utils.timeout(20); + + const should_be_active = document.querySelector("input[name=i2]"); + expect(document.activeElement).toBe(should_be_active); + + done(); + }); +}); diff --git a/src/pat/autofocus/index-iframed.html b/src/pat/autofocus/index-iframed.html new file mode 100644 index 000000000..0ac54c538 --- /dev/null +++ b/src/pat/autofocus/index-iframed.html @@ -0,0 +1,21 @@ + + +
+ +Elements in the iframe below should not get the focus.
+ +