UNPKG

3.58 kBJavaScriptView Raw
1// defined by w3c
2var DOCUMENT_NODE = 9
3
4/**
5 * Returns `true` if `w` is a Document object, or `false` otherwise.
6 *
7 * @param {?} d - Document object, maybe
8 * @return {Boolean}
9 * @private
10 */
11
12function isDocument(d) {
13 return d && d.nodeType === DOCUMENT_NODE
14}
15
16/**
17 * Returns the `document` object associated with the given `node`, which may be
18 * a DOM element, the Window object, a Selection, a Range. Basically any DOM
19 * object that references the Document in some way, this function will find it.
20 *
21 * @param {Mixed} node - DOM node, selection, or range in which to find the `document` object
22 * @return {Document} the `document` object associated with `node`
23 * @public
24 */
25
26function getDocument(node) {
27 if (isDocument(node)) {
28 return node
29 } else if (isDocument(node.ownerDocument)) {
30 return node.ownerDocument
31 } else if (isDocument(node.document)) {
32 return node.document
33 } else if (node.parentNode) {
34 return getDocument(node.parentNode)
35
36 // Range support
37 } else if (node.commonAncestorContainer) {
38 return getDocument(node.commonAncestorContainer)
39 } else if (node.startContainer) {
40 return getDocument(node.startContainer)
41
42 // Selection support
43 } else if (node.anchorNode) {
44 return getDocument(node.anchorNode)
45 }
46}
47
48function withinElement(child, parent) {
49 // don't throw if `child` is null
50 if (!child) return false
51
52 // Range support
53 if (child.commonAncestorContainer) child = child.commonAncestorContainer
54 else if (child.endContainer) child = child.endContainer
55 // ask the browser if parent contains child
56
57 if (child === window) return true
58
59 return parent.contains(child)
60}
61
62module.exports = function offset(el) {
63 var doc = getDocument(el)
64 if (!doc) return
65
66 // Make sure it's not a disconnected DOM node
67 if (!withinElement(el, doc)) return
68
69 var body = doc.body
70 if (body === el) {
71 return bodyOffset(el)
72 }
73
74 var box = { top: 0, left: 0 }
75 if (typeof el.getBoundingClientRect !== "undefined") {
76 // If we don't have gBCR, just use 0,0 rather than error
77 // BlackBerry 5, iOS 3 (original iPhone)
78 box = el.getBoundingClientRect()
79
80 if (el.collapsed && box.left === 0 && box.top === 0) {
81 // collapsed Range instances sometimes report 0, 0
82 // see: http://stackoverflow.com/a/6847328/376773
83 var span = doc.createElement("span")
84
85 // Ensure span has dimensions and position by
86 // adding a zero-width space character
87 span.appendChild(doc.createTextNode("\u200b"))
88 el.insertNode(span)
89 box = span.getBoundingClientRect()
90
91 // Remove temp SPAN and glue any broken text nodes back together
92 var spanParent = span.parentNode
93 spanParent.removeChild(span)
94 spanParent.normalize()
95 }
96 }
97
98 var docEl = doc.documentElement
99 var clientTop = docEl.clientTop || body.clientTop || 0
100 var clientLeft = docEl.clientLeft || body.clientLeft || 0
101 var scrollTop = window.pageYOffset || docEl.scrollTop
102 var scrollLeft = window.pageXOffset || docEl.scrollLeft
103
104 return {
105 top: box.top + scrollTop - clientTop,
106 left: box.left + scrollLeft - clientLeft
107 }
108}
109
110function bodyOffset(body) {
111 var top = body.offsetTop
112 var left = body.offsetLeft
113
114 top += parseFloat(body.style.marginTop || 0)
115 left += parseFloat(body.style.marginLeft || 0)
116
117 return {
118 top: top,
119 left: left
120 }
121}