-
Notifications
You must be signed in to change notification settings - Fork 14
/
scrollingelement.js
112 lines (104 loc) · 4.06 KB
/
scrollingelement.js
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
/*! https://mths.be/scrollingelement v1.5.2 by @diegoperini & @mathias | MIT license */
if (!('scrollingElement' in document)) (function() {
function computeStyle(element) {
if (window.getComputedStyle) {
// Support Firefox < 4 which throws on a single parameter.
return getComputedStyle(element, null);
}
// Support Internet Explorer < 9.
return element.currentStyle;
}
function isBodyElement(element) {
// The `instanceof` check gives the correct result for e.g. `body` in a
// non-HTML namespace.
if (window.HTMLBodyElement) {
return element instanceof HTMLBodyElement;
}
// Fall back to a `tagName` check for old browsers.
return /body/i.test(element.tagName);
}
function getNextBodyElement(frameset) {
// We use this function to be correct per spec in case `document.body` is
// a `frameset` but there exists a later `body`. Since `document.body` is
// a `frameset`, we know the root is an `html`, and there was no `body`
// before the `frameset`, so we just need to look at siblings after the
// `frameset`.
var current = frameset;
while (current = current.nextSibling) {
if (current.nodeType == 1 && isBodyElement(current)) {
return current;
}
}
// No `body` found.
return null;
}
// Note: standards mode / quirks mode can be toggled at runtime via
// `document.write`.
var isCompliantCached;
var isCompliant = function() {
var isStandardsMode = /^CSS1/.test(document.compatMode);
if (!isStandardsMode) {
// In quirks mode, the result is equivalent to the non-compliant
// standards mode behavior.
return false;
}
if (isCompliantCached === void 0) {
// When called for the first time, check whether the browser is
// standard-compliant, and cache the result.
var iframe = document.createElement('iframe');
iframe.style.height = '1px';
(document.body || document.documentElement || document).appendChild(iframe);
var doc = iframe.contentWindow.document;
doc.write('<!DOCTYPE html><div style="height:9999em">x</div>');
doc.close();
isCompliantCached = doc.documentElement.scrollHeight > doc.body.scrollHeight;
iframe.parentNode.removeChild(iframe);
}
return isCompliantCached;
};
function isRendered(style) {
return style.display != 'none' && !(style.visibility == 'collapse' &&
/^table-(.+-group|row|column)$/.test(style.display));
}
function isScrollable(body) {
// A `body` element is scrollable if `body` and `html` both have
// non-`visible` overflow and are both being rendered.
var bodyStyle = computeStyle(body);
var htmlStyle = computeStyle(document.documentElement);
return bodyStyle.overflow != 'visible' && htmlStyle.overflow != 'visible' &&
isRendered(bodyStyle) && isRendered(htmlStyle);
}
var scrollingElement = function() {
if (isCompliant()) {
return document.documentElement;
}
var body = document.body;
// Note: `document.body` could be a `frameset` element, or `null`.
// `tagName` is uppercase in HTML, but lowercase in XML.
var isFrameset = body && !/body/i.test(body.tagName);
body = isFrameset ? getNextBodyElement(body) : body;
// If `body` is itself scrollable, it is not the `scrollingElement`.
return body && isScrollable(body) ? null : body;
};
if (Object.defineProperty) {
// Support modern browsers that lack a native implementation.
Object.defineProperty(document, 'scrollingElement', {
'get': scrollingElement
});
} else if (document.__defineGetter__) {
// Support Firefox ≤ 3.6.9, Safari ≤ 4.1.3.
document.__defineGetter__('scrollingElement', scrollingElement);
} else {
// IE ≤ 4 lacks `attachEvent`, so it only gets this one assignment. IE ≤ 7
// gets it too, but the value is updated later (see `propertychange`).
document.scrollingElement = scrollingElement();
document.attachEvent && document.attachEvent('onpropertychange', function() {
// This is for IE ≤ 7 only.
// A `propertychange` event fires when `<body>` is parsed because
// `document.activeElement` then changes.
if (window.event.propertyName == 'activeElement') {
document.scrollingElement = scrollingElement();
}
});
}
}());