Blame view

lib/jsdom/living/helpers/focusing.js 2.47 KB
858f2bdf5   Boyan Georgiev   fixes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
  "use strict";
  const nodeType = require("../node-type.js");
  const FocusEvent = require("../generated/FocusEvent.js");
  const idlUtils = require("../generated/utils.js");
  const { isDisabled } = require("./form-controls.js");
  const { firstChildWithLocalName } = require("./traversal");
  const { createAnEvent } = require("./events");
  const { HTML_NS } = require("./namespaces");
  
  const focusableFormElements = new Set(["input", "select", "textarea", "button"]);
  
  // https://html.spec.whatwg.org/multipage/interaction.html#focusable-area, but also some of
  // https://html.spec.whatwg.org/multipage/interaction.html#focusing-steps: e.g., Documents are not actually focusable
  // areas, but their viewports are, and the first step of the latter algorithm translates Documents to their viewports.
  // And also https://html.spec.whatwg.org/multipage/interaction.html#specially-focusable!
  exports.isFocusableAreaElement = elImpl => {
    if (!elImpl._ownerDocument._defaultView && !elImpl._defaultView) {
      return false;
    }
  
    if (elImpl._nodeType === nodeType.DOCUMENT_NODE) {
      return true;
    }
  
    if (!Number.isNaN(parseInt(elImpl.getAttributeNS(null, "tabindex")))) {
      return true;
    }
  
    if (elImpl._namespaceURI === HTML_NS) {
      if (elImpl._localName === "iframe") {
        return true;
      }
  
      if (elImpl._localName === "a" && elImpl.hasAttributeNS(null, "href")) {
        return true;
      }
  
      if (elImpl._localName === "summary" && elImpl.parentNode &&
          elImpl.parentNode._localName === "details" &&
          elImpl === firstChildWithLocalName(elImpl.parentNode, "summary")) {
        return true;
      }
  
      if (focusableFormElements.has(elImpl._localName) && !isDisabled(elImpl)) {
        if (elImpl._localName === "input" && elImpl.type === "hidden") {
          return false;
        }
  
        return true;
      }
    }
  
    return false;
  };
  
  // https://html.spec.whatwg.org/multipage/interaction.html#fire-a-focus-event plus the steps of
  // https://html.spec.whatwg.org/multipage/interaction.html#focus-update-steps that adjust Documents to Windows
  exports.fireFocusEventWithTargetAdjustment = (name, target, relatedTarget) => {
    if (target === null) {
      // E.g. firing blur with nothing previously focused.
      return;
    }
  
    const event = createAnEvent(name, target._globalObject, FocusEvent, {
      composed: true,
      relatedTarget,
      view: target._ownerDocument._defaultView,
      detail: 0
    });
  
    if (target._defaultView) {
      target = idlUtils.implForWrapper(target._defaultView);
    }
  
    target._dispatch(event);
  };