-
Notifications
You must be signed in to change notification settings - Fork 14
/
mainonly.js
185 lines (168 loc) · 6.98 KB
/
mainonly.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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
// A JavaScript bookmarklet designed to isolate and highlight a specific element on a webpage, effectively hiding all other elements.
(function () {
// if re-run on the same page, remove the previous instance
if (document.getElementById("mainonly")) {
document.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape" }));
}
var selectedElement = document.body;
var lastStrategy = null; // which strategy is used to select the element
// strategy overview
// 1. if the selected element doesn't has `id`, then use `id`
// (since it fixed the issue of pure text nodes can not be styled with CSS)
// 2. otherwise fallback to use `class`
if (!selectedElement.id) {
// id
lastStrategy = 'id';
selectedElement.id = "mainonly";
} else {
// class
lastStrategy = 'class';
selectedElement.classList.add("mainonly");
}
const style = document.head.appendChild(document.createElement("style"));
style.textContent = "#mainonly { outline: 2px solid red; } .mainonly { outline: 2px solid red; }";
// selection guide overlay
const guideTextCn = '正在选择元素。按 <kbd>Esc</kbd> 键取消选择。向下滚动,或按下 <kbd>=</kbd>/<kbd>.</kbd> 键缩小选区。向上滚动,或按下 <kbd>-</kbd>/<kbd>,</kbd> 键扩大选区。'
const guideTextEn = 'Selecting element. Press <kbd>Esc</kbd> to cancel selection. Scroll down, or press <kbd>=</kbd>/<kbd>.</kbd> to shrink the selection. Scroll up, or press <kbd>-</kbd>/<kbd>,</kbd>, to expand the selection.'
const guide = document.body.appendChild(document.createElement("div"));
guide.className = "mainonly-guide";
guide.innerHTML = `<p>${guideTextCn}</p><p>${guideTextEn}</p>`;
const guideStyle = document.head.appendChild(document.createElement("style"));
guideStyle.textContent = `
.mainonly-guide {
position: fixed;
top: 0;
left: 50%; /* center the box horizontally */
transform: translate(-50%, 0); /* center the box horizontally */
padding: 0.5rem;
font-size: 1rem;
font-family: sans-serif;
text-align: center;
color: white;
background-color: rgba(0, 0, 0, 0.5);
border-radius: 0.5em;
z-index: 999999999;
kbd {
display: inline-block;
padding: 0.1em 0.3em;
font-size: 0.8em;
line-height: 1;
color: #24292e;
vertical-align: middle;
background-color: #fafbfc;
border: 1px solid #d1d5da;
border-radius: 3px;
box-shadow: inset 0 -1px 0 #d1d5da;
}
}`;
/** @param {*} element */
function outlineElement(element) {
if (element instanceof HTMLElement) { // Ignores non-HTMLElements
// deselect previous element
if (lastStrategy === 'id') {
// id
selectedElement.removeAttribute("id");
} else {
// class
selectedElement.classList.remove("mainonly");
}
// select the new selected element
selectedElement = element;
if (!selectedElement.id) {
// id
lastStrategy = 'id';
selectedElement.id = "mainonly";
} else {
// class
lastStrategy = 'class';
selectedElement.classList.add("mainonly");
}
}
}
/** @param {MouseEvent} event */
function onMouseOver(event) {
outlineElement(event.target);
}
/** @param {MouseEvent} event */
function onClick(event) {
event.preventDefault();
markParents();
if (lastStrategy === 'id') {
// id
style.textContent = `* { visibility: hidden; } #mainonly, #mainonly *, .mainonly_parents { visibility: visible; }`;
} else {
// class
style.textContent = `* { visibility: hidden; } .mainonly, .mainonly *, .mainonly_parents { visibility: visible; }`;
}
cleanupEventListeners();
hideGuideOverlay();
}
function hideGuideOverlay() {
guide.remove();
guideStyle.remove();
}
function markParents() {
var parents = selectedElement;
while (parents.parentElement) {
parents = parents.parentElement;
parents.classList.add("mainonly_parents");
}
}
function removeParents() {
var parents = document.querySelectorAll(".mainonly_parents");
for (var i = 0; i < parents.length; i++) {
parents[i].classList.remove("mainonly_parents");
}
}
/** @param {KeyboardEvent} event */
function onKeydown(event) {
if (event.key === "Escape") {
// Recover the hidden elements
style.remove();
document.removeEventListener("keydown", onKeydown);
cleanupEventListeners();
hideGuideOverlay();
// Restore the selected element to its original state
if (lastStrategy === 'id') {
// id
selectedElement.removeAttribute("id");
} else {
// class
selectedElement.classList.remove("mainonly");
}
removeParents();
} else if (event.key === ',' || event.key === '-') {
// up, select parent element
outlineElement(selectedElement.parentElement);
} else if (event.key === '.' || event.key === '=') {
// down, select first child element
var childElement = selectedElement.querySelector(":hover");
if (childElement) {
outlineElement(childElement);
}
}
}
/** @param {WheelEvent} event */
function onWheel(event) {
event.preventDefault();
if (event.deltaY < 0) {
// Scrolling up, select parent element
outlineElement(selectedElement.parentElement);
} else {
// Scrolling down, select child element containing the cursor
var childElement = selectedElement.querySelector(":hover");
if (childElement) {
outlineElement(childElement);
}
}
}
function cleanupEventListeners() {
document.removeEventListener("mouseover", onMouseOver);
document.removeEventListener("click", onClick);
document.removeEventListener("wheel", onWheel);
}
document.addEventListener("mouseover", onMouseOver);
document.addEventListener("click", onClick);
document.addEventListener("wheel", onWheel, { passive: false });
document.addEventListener("keydown", onKeydown);
})();