UNPKG

3.43 kBJavaScriptView Raw
1/* eslint no-param-reassign: 0 */
2
3import { window, isServer } from "./platform"
4import { noop } from "./utils"
5
6
7
8const requestAnimationFrame = isServer
9 ? noop
10 : window.requestAnimationFrame ||
11 window.mozRequestAnimationFrame ||
12 window.webkitRequestAnimationFrame ||
13 ((fn) => { window.setTimeout(fn, 20) })
14
15const cancelAnimationFrame = isServer
16 ? noop
17 : window.cancelAnimationFrame ||
18 window.mozCancelAnimationFrame ||
19 window.webkitCancelAnimationFrame ||
20 window.clearTimeout
21
22const isIE = isServer ? false : navigator.userAgent.match(/Trident/)
23
24const namespace = "__resizeDetector__"
25
26
27
28const uninitialize = (el) => {
29 el[namespace].destroy()
30 el[namespace] = undefined
31}
32
33
34
35const createElementHack = () => {
36 const el = document.createElement("object")
37 el.className = "resize-sensor"
38 el.setAttribute("style", "display: block; position: absolute; top: 0; left: 0; height: 100%; width: 100%; overflow: hidden; pointer-events: none; z-index: -1;")
39 el.setAttribute("class", "resize-sensor")
40 el.type = "text/html"
41 el.data = "about:blank"
42 return el
43}
44
45
46
47const initialize = (el) => {
48
49 const detector = el[namespace] = {}
50 detector.listeners = []
51
52 const onResize = (e) => {
53 /* Keep in mind e.target could be el OR objEl. In this current implementation we don't seem to need to know this but its important
54 to not forget e.g. in some future refactoring scenario. */
55 if (detector.resizeRAF) cancelAnimationFrame(detector.resizeRAF)
56 detector.resizeRAF = requestAnimationFrame(() => {
57 detector.listeners.forEach((fn) => { fn(e) })
58 })
59 }
60
61 if (isIE) {
62 /* We do not support ie8 and below (or ie9 in compat mode).
63 Therefore there is no presence of `attachEvent` here. */
64 el.addEventListener("onresize", onResize)
65 detector.destroy = () => {
66 el.removeEventListener("onresize", onResize)
67 }
68 } else {
69 if (getComputedStyle(el).position === "static") {
70 detector.elWasStaticPosition = true
71 el.style.position = "relative"
72 }
73 const objEl = createElementHack()
74 objEl.onload = function (/* event */) {
75 this.contentDocument.defaultView.addEventListener("resize", onResize)
76 }
77 detector.destroy = () => {
78 if (detector.elWasStaticPosition) el.style.position = ""
79 // Event handlers will be automatically removed.
80 // http://stackoverflow.com/questions/12528049/if-a-dom-element-is-removed-are-its-listeners-also-removed-from-memory
81 el.removeChild(objEl)
82 }
83
84 el.appendChild(objEl)
85 }
86}
87
88
89
90const on = (el, fn) => {
91
92 /* Window object natively publishes resize events. We handle it as a
93 special case here so that users do not have to think about two APIs. */
94
95 if (el === window) {
96 window.addEventListener("resize", fn)
97 return
98 }
99
100 /* Not caching namespace read here beacuse not guaranteed that its available. */
101
102 if (!el[namespace]) initialize(el)
103 el[namespace].listeners.push(fn)
104}
105
106
107
108const off = (el, fn) => {
109 if (el === window) {
110 window.removeEventListener("resize", fn)
111 return
112 }
113 const detector = el[namespace]
114 if (!detector) return
115 const i = detector.listeners.indexOf(fn)
116 if (i !== -1) detector.listeners.splice(i, 1)
117 if (!detector.listeners.length) uninitialize(el)
118}
119
120
121
122export default {
123 on,
124 off,
125 addEventListener: on,
126 removeEventListener: off,
127}
128export {
129 on,
130 off,
131 on as addEventListener,
132 off as removeEventListener,
133}