Skip to content

Commit 622bf54

Browse files
authored
Merge pull request #1214 from Patternslib/document_ready
Implement document_ready as replacement for $
2 parents 7ace4ce + e8c8b4b commit 622bf54

File tree

4 files changed

+108
-2
lines changed

4 files changed

+108
-2
lines changed

src/core/dom.js

+26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/* Utilities for DOM traversal or navigation */
2+
import events from "./events";
23
import logging from "./logging";
34
import create_uuid from "./uuid";
45

@@ -9,6 +10,30 @@ const DATA_STYLE_DISPLAY = "__patternslib__style__display";
910

1011
const INPUT_SELECTOR = "input, select, textarea, button";
1112

13+
/**
14+
* Wait for the document to be ready.
15+
*
16+
* @param {Function} fn - The function to call when the document is ready.
17+
*/
18+
const document_ready = (fn) => {
19+
const event_id = create_uuid();
20+
21+
const _ready = () => {
22+
if (document.readyState !== "loading") {
23+
// Remove the event listener for this callback.
24+
events.remove_event_listener(document, event_id);
25+
// call on next available tick
26+
setTimeout(fn, 1);
27+
}
28+
};
29+
30+
// Listen for the document to be ready and call _ready() when it is.
31+
events.add_event_listener(document, "readystatechange", event_id, _ready);
32+
33+
// Also check the ready state immediately in case we missed the event.
34+
_ready();
35+
};
36+
1237
/**
1338
* Return an array of DOM nodes.
1439
*
@@ -575,6 +600,7 @@ const find_inputs = (el) => {
575600
};
576601

577602
const dom = {
603+
document_ready: document_ready,
578604
toNodeArray: toNodeArray,
579605
querySelectorAllAndMe: querySelectorAllAndMe,
580606
wrap: wrap,

src/core/dom.test.js

+80
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,91 @@
11
import $ from "jquery";
22
import dom from "./dom";
3+
import utils from "./utils";
34

45
describe("core.dom tests", () => {
56
// Tests from the core.dom module
67

78
afterEach(() => {
89
document.body.innerHTML = "";
10+
jest.restoreAllMocks();
11+
});
12+
13+
describe("document_ready", () => {
14+
it("calls the callback, once the document is ready.", async () => {
15+
let cnt = 0;
16+
const counter = () => {
17+
cnt++;
18+
};
19+
20+
// Call document ready immediately. It should already call the
21+
// callback, if ready. Which it isn't.
22+
jest.spyOn(document, "readyState", "get").mockReturnValue("loading");
23+
dom.document_ready(counter);
24+
await utils.timeout(1);
25+
expect(cnt).toBe(0);
26+
27+
// While readyState "loading" the callback should not be called.
28+
document.dispatchEvent(new Event("readystatechange"));
29+
await utils.timeout(1);
30+
expect(cnt).toBe(0);
31+
32+
// While still loading the callback should still not be called.
33+
document.dispatchEvent(new Event("readystatechange"));
34+
await utils.timeout(1);
35+
expect(cnt).toBe(0);
36+
37+
// Now it's the time.
38+
jest.spyOn(document, "readyState", "get").mockReturnValue("complete");
39+
document.dispatchEvent(new Event("readystatechange"));
40+
await utils.timeout(1);
41+
expect(cnt).toBe(1);
42+
43+
// But the callback is only called once and the event handler removed from the document.
44+
document.dispatchEvent(new Event("readystatechange"));
45+
await utils.timeout(1);
46+
expect(cnt).toBe(1);
47+
});
48+
49+
it("it will also fire on readyState interactive, not only complete.", async () => {
50+
let cnt = 0;
51+
const counter = () => {
52+
cnt++;
53+
};
54+
55+
// Call document ready immediately. It should already call the
56+
// callback, if ready. Which it isn't.
57+
jest.spyOn(document, "readyState", "get").mockReturnValue("loading");
58+
dom.document_ready(counter);
59+
await utils.timeout(1);
60+
expect(cnt).toBe(0);
61+
62+
// When readyState interactive, the callback should be called.
63+
jest.spyOn(document, "readyState", "get").mockReturnValue("interactive");
64+
document.dispatchEvent(new Event("readystatechange"));
65+
await utils.timeout(1);
66+
expect(cnt).toBe(1);
67+
});
68+
69+
it("the callback will be called immedeately if the ready state change has already happended.", async () => {
70+
let cnt = 0;
71+
const counter = () => {
72+
cnt++;
73+
};
74+
75+
// Call document ready immediately. It should already call the
76+
// callback, if ready. Which it isn't.
77+
jest.spyOn(document, "readyState", "get").mockReturnValue("complete");
78+
dom.document_ready(counter);
79+
await utils.timeout(1);
80+
expect(cnt).toBe(1);
81+
82+
// But another state change would not call the callback, because
83+
// the event listener is already de-registered.
84+
jest.spyOn(document, "readyState", "get").mockReturnValue("interactive");
85+
document.dispatchEvent(new Event("readystatechange"));
86+
await utils.timeout(1);
87+
expect(cnt).toBe(1);
88+
});
989
});
1090

1191
describe("toNodeArray tests", () => {

src/core/registry.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ const registry = {
6161
// the DOM is scanned. After that registering a new pattern
6262
// results in rescanning the DOM only for this pattern.
6363
init() {
64-
$(document).ready(function () {
64+
dom.document_ready(() => {
6565
if (window.__patternslib_registry_initialized) {
6666
// Do not reinitialize a already initialized registry.
6767
return;

src/pat/markdown/markdown.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ class Pattern extends BasePattern {
104104
}
105105
}
106106

107-
$(document).ready(function () {
107+
dom.document_ready(() => {
108108
$(document.body).on(
109109
"patterns-inject-triggered.pat-markdown",
110110
"a.pat-inject",

0 commit comments

Comments
 (0)