Skip to content

Commit 4389052

Browse files
authored
Merge pull request #47 from keithamus/add-checkvisibility
add checkVisibility
2 parents 8a87ec7 + a5df092 commit 4389052

File tree

4 files changed

+173
-3
lines changed

4 files changed

+173
-3
lines changed

docs/index.html

+26-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,22 @@
1-
<!DOCTYPE html>
1+
<!doctype html>
22
<html>
33
<head>
44
<title>GitHub Feature Support Table</title>
55
<meta charset="utf-8" />
66
<link rel="shortcut icon" href="favicon.ico" type="image/x-icon" />
77
<style>
88
html {
9-
font-family: 'Alliance No.1', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif,
10-
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
9+
font-family:
10+
'Alliance No.1',
11+
-apple-system,
12+
BlinkMacSystemFont,
13+
'Segoe UI',
14+
Helvetica,
15+
Arial,
16+
sans-serif,
17+
'Apple Color Emoji',
18+
'Segoe UI Emoji',
19+
'Segoe UI Symbol';
1120
font-weight: 400;
1221
line-height: 28px;
1322
}
@@ -630,6 +639,20 @@ <h1>GitHub Feature Support Table</h1>
630639
<td data-supported="true"><div>78+</div></td>
631640
<td data-supported="true"><div>16.0+</div></td>
632641
</tr>
642+
<tr>
643+
<th>
644+
<a href="https://developer.mozilla.org/en-US/docs/Web/API/Element/checkVisibility">
645+
<code>Element.checkVisibility</code>
646+
</a>
647+
</th>
648+
<td data-polyfill="elementCheckVisibility"><div>*</div></td>
649+
<td data-supported="true"><div>105+</div></td>
650+
<td data-supported="true"><div>105+</div></td>
651+
<td data-supported="true"><div>106+</div></td>
652+
<td data-supported="false"><div>*</div></td>
653+
<td data-supported="true"><div>91+</div></td>
654+
<td data-supported="true"><div>20.0+</div></td>
655+
</tr>
633656
<tr>
634657
<th>
635658
<a

src/element-checkvisibility.ts

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
declare global {
2+
interface Element {
3+
checkVisibility(options?: Partial<CheckVisibilityOptions>): boolean
4+
}
5+
}
6+
7+
interface CheckVisibilityOptions {
8+
checkOpacity: boolean
9+
checkVisibilityCSS: boolean
10+
}
11+
12+
export function checkVisibility(
13+
this: Element,
14+
{checkOpacity = false, checkVisibilityCSS = false}: Partial<CheckVisibilityOptions> = {},
15+
) {
16+
if (!this.isConnected) return false
17+
const styles = getComputedStyle(this)
18+
if (styles.getPropertyValue('display') === 'contents') return false
19+
if (checkVisibilityCSS && styles.getPropertyValue('visibility') !== 'visible') return false
20+
// eslint-disable-next-line @typescript-eslint/no-this-alias
21+
let node: Element | null = this
22+
while (node) {
23+
const nodeStyles = node === this ? styles : getComputedStyle(node)
24+
if (nodeStyles.getPropertyValue('display') === 'none') return false
25+
if (checkOpacity && nodeStyles.getPropertyValue('opacity') === '0') return false
26+
if (node !== this && nodeStyles.getPropertyValue('content-visibility') === 'hidden') {
27+
return false
28+
}
29+
if (!node.parentElement && node.getRootNode() instanceof ShadowRoot) {
30+
node = (node.getRootNode() as ShadowRoot).host
31+
} else {
32+
node = node.parentElement
33+
}
34+
}
35+
return true
36+
}
37+
38+
export function isSupported(): boolean {
39+
return 'checkVisibility' in Element.prototype && typeof Element.prototype.checkVisibility === 'function'
40+
}
41+
42+
export function isPolyfilled(): boolean {
43+
return Element.prototype.checkVisibility === checkVisibility
44+
}
45+
46+
export function apply(): void {
47+
if (!isSupported()) {
48+
Element.prototype.checkVisibility = checkVisibility
49+
}
50+
}

src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as abortSignalTimeout from './abortsignal-timeout.js'
33
import * as arrayAt from './arraylike-at.js'
44
import * as clipboardItem from './clipboarditem.js'
55
import * as cryptoRandomUUID from './crypto-randomuuid.js'
6+
import * as elementCheckVisibility from './element-checkvisibility.js'
67
import * as eventAbortSignal from './event-abortsignal.js'
78
import * as navigatorClipboard from './navigator-clipboard.js'
89
import * as formRequestSubmit from './form-requestsubmit.js'
@@ -58,6 +59,7 @@ export const polyfills = {
5859
arrayAt,
5960
clipboardItem,
6061
cryptoRandomUUID,
62+
elementCheckVisibility,
6163
eventAbortSignal,
6264
navigatorClipboard,
6365
formRequestSubmit,

test/element-checkvisibility.js

+95
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
import {apply, isPolyfilled, isSupported, checkVisibility} from '../lib/element-checkvisibility.js'
2+
3+
describe('checkVisibility', () => {
4+
it('has standard isSupported, isPolyfilled, apply API', () => {
5+
expect(isSupported).to.be.a('function')
6+
expect(isPolyfilled).to.be.a('function')
7+
expect(apply).to.be.a('function')
8+
expect(isSupported()).to.be.a('boolean')
9+
expect(isPolyfilled()).to.equal(false)
10+
})
11+
12+
it('checks visibility of elements', async () => {
13+
// These tests originate from
14+
// https://github.com/web-platform-tests/wpt/blob/master/css/cssom-view/checkVisibility.html
15+
const el = document.createElement('div')
16+
// eslint-disable-next-line github/no-inner-html
17+
el.innerHTML = `
18+
<div id=visibilityhidden style="visibility:hidden">hello</div>
19+
<div style="content-visibility:hidden">
20+
<div id=cvhidden>hello</div>
21+
</div>
22+
<div style="content-visibility:auto">
23+
<div id=cvauto>hello</div>
24+
</div>
25+
<div id=displaynone style="display:none">hello</div>
26+
<div style="display:none" class="shadow-host-with-slot">
27+
<div id="slottedindisplaynone" slot="slot">slotted</div>
28+
</div>
29+
<div id=displaycontents style="display:contents">
30+
<div id=displaycontentschild>hello</div>
31+
</div>
32+
<div id=opacityzero style="opacity:0">hello</div>
33+
<div style="opacity:0" class="shadow-host-with-slot">
34+
<div id="slottedinopacityzero" slot="slot">slotted</div>
35+
</div>
36+
<div style="content-visibility:hidden">
37+
<div id=cvhiddenchildwithupdate></div>
38+
</div>
39+
<div style="content-visibility:hidden" id=cvhiddenwithupdate></div>
40+
<div style="content-visibility:hidden" class="shadow-host-with-slot">
41+
<div id="slottedincvhidden" slot="slot">slotted</div>
42+
</div>
43+
<div style="height:10000px">spacer</div>
44+
<div style="content-visibility:auto">
45+
<div id=cvautooffscreen>hello</div>
46+
</div>
47+
<div id=cvautocontainer>
48+
<div id=cvautochild></div>
49+
</div>
50+
<div style="content-visibility:auto">
51+
<div style="content-visibility:auto">
52+
<div id=nestedcvautochild></div>
53+
</div>
54+
`
55+
document.body.append(el)
56+
for (const host of document.querySelectorAll('.shadow-host-with-slot')) {
57+
const shadowRoot = host.attachShadow({mode: 'open'})
58+
const slot = document.createElement('slot')
59+
slot.name = 'slot'
60+
shadowRoot.appendChild(slot)
61+
}
62+
expect(checkVisibility.call(document.getElementById('visibilityhidden'), {checkVisibilityCSS: true})).to.equal(
63+
false,
64+
)
65+
expect(checkVisibility.call(document.getElementById('visibilityhidden'), {checkVisibilityCSS: false})).to.equal(
66+
true,
67+
)
68+
expect(checkVisibility.call(document.getElementById('cvhidden'))).to.equal(false)
69+
expect(checkVisibility.call(document.getElementById('slottedincvhidden'))).to.equal(false)
70+
expect(checkVisibility.call(document.getElementById('cvauto'))).to.equal(true)
71+
expect(checkVisibility.call(document.getElementById('cvautooffscreen'))).to.equal(true)
72+
expect(checkVisibility.call(document.getElementById('displaynone'))).to.equal(false)
73+
expect(checkVisibility.call(document.getElementById('slottedindisplaynone'))).to.equal(false)
74+
expect(checkVisibility.call(document.getElementById('displaycontents'))).to.equal(false)
75+
expect(checkVisibility.call(document.getElementById('displaycontentschild'))).to.equal(true)
76+
expect(checkVisibility.call(document.getElementById('opacityzero'), {checkOpacity: true})).to.equal(false)
77+
expect(checkVisibility.call(document.getElementById('opacityzero'), {checkOpacity: false})).to.equal(true)
78+
expect(checkVisibility.call(document.getElementById('slottedinopacityzero'), {checkOpacity: true})).to.equal(false)
79+
expect(checkVisibility.call(document.getElementById('slottedinopacityzero'), {checkOpacity: false})).to.equal(true)
80+
const cvautocontainer = document.getElementById('cvautocontainer')
81+
const cvautochild = document.getElementById('cvautochild')
82+
cvautocontainer.style.contentVisibility = 'auto'
83+
cvautochild.style.visibility = 'hidden'
84+
expect(checkVisibility.call(cvautochild, {checkVisibilityCSS: true})).to.equal(false)
85+
cvautochild.style.visibility = 'visible'
86+
expect(checkVisibility.call(cvautochild, {checkVisibilityCSS: true})).to.equal(true)
87+
expect(checkVisibility.call(document.getElementById('nestedcvautochild'))).to.equal(true)
88+
const cvhiddenchildwithupdate = document.getElementById('cvhiddenchildwithupdate')
89+
cvhiddenchildwithupdate.getBoundingClientRect()
90+
expect(checkVisibility.call(cvhiddenchildwithupdate)).to.equal(false)
91+
const cvhiddenwithupdate = document.getElementById('cvhiddenwithupdate')
92+
cvhiddenwithupdate.getBoundingClientRect()
93+
expect(checkVisibility.call(cvhiddenwithupdate)).to.equal(true)
94+
})
95+
})

0 commit comments

Comments
 (0)