Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
62 changes: 36 additions & 26 deletions src/pat/autofocus/autofocus.js
Original file line number Diff line number Diff line change
@@ -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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pilz: requested fix.

// 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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pilz: We want the global event handler registered only once.

// 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) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pilz: If there is already a setFocus task scheduled, exit. No need to do it multiple times as this method operates on the whole DOM tree and is not scoped for the element itself.

return;
}
const $all = $(target);
const visible = [...$all].filter((it) => $(it).is(":visible"));
const empty = visible.filter((it) => it.value === "");
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@pilz: this fix is for the documented behavior (in documentation.md + the index.html) to not autofocus if the input is not empty and there is another autofocus element.

const el = empty[0] || visible[0];
if (el) {
scheduled_task = setTimeout(() => {
el.focus();
scheduled_task = null;
}, 10);
}
},
});
46 changes: 46 additions & 0 deletions src/pat/autofocus/autofocus.test.js
Original file line number Diff line number Diff line change
@@ -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 = `
<input name="i1" type="text" class="pat-autofocus"/>
<input name="i2" type="text" class="pat-autofocus"/>
<input name="i3" type="text" class="pat-autofocus"/>
`;
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 = `
<input name="i1" type="text" class="pat-autofocus" value="okay"/>
<input name="i2" type="text" class="pat-autofocus"/>
<input name="i3" type="text" class="pat-autofocus"/>
`;
pattern.init(container);
await utils.timeout(20);

const should_be_active = document.querySelector("input[name=i2]");
expect(document.activeElement).toBe(should_be_active);

done();
});
});
21 changes: 21 additions & 0 deletions src/pat/autofocus/index-iframed.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Focus demo page</title>
<link rel="stylesheet" href="/style/common.css" type="text/css" />
<script src="/dist/bundle.js" type="text/javascript"></script>
<style type="text/css" media="screen">
form {
margin-top: 400px;
}
</style>
</head>
<body>
<form action="">
<label>Title
<input class="pat-autofocus" type="text" />
</label>
</form>
</body>
</html>
51 changes: 21 additions & 30 deletions src/pat/autofocus/index.html
Original file line number Diff line number Diff line change
@@ -1,33 +1,24 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Focus demo page</title>
<link rel="stylesheet" href="/style/common.css" type="text/css" />
<script
src="/dist/bundle.js"
type="text/javascript"
charset="utf-8"
></script>
</head>
<body>
<form action="">
<fieldset class="horizontal">
<label>
Title
<input
class="pat-autofocus"
type="text"
value="Prefilled title" /></label
><br />
<label>
Keywords
<input
class="pat-autofocus"
type="text"
placeholder="This field should get the focus"
/></label>
</fieldset>
</form>
</body>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Focus demo page</title>
<link rel="stylesheet" href="/style/common.css" type="text/css" />
<script src="/dist/bundle.js" type="text/javascript"></script>
</head>
<body>
<form action="">
<fieldset class="horizontal">
<label>Title
<input class="pat-autofocus" type="text" value="Prefilled title" />
</label>
<br />
<label>Keywords
<input class="pat-autofocus" type="text" placeholder="This field should get the focus" />
</label>
</fieldset>
</form>
<p>Elements in the iframe below should not get the focus.</p>
<iframe src="./index-iframed.html" frameborder="0" style="width: 400px; height: 300px; border: 1px solid black;"></iframe>
</body>
</html>