Update dev-tools.js
Browse files- dev-tools.js +475 -25
dev-tools.js
CHANGED
@@ -66,7 +66,7 @@
|
|
66 |
}
|
67 |
|
68 |
.devtools-panel.active {
|
69 |
-
display:
|
70 |
}
|
71 |
|
72 |
/* Console スタイル */
|
@@ -74,6 +74,7 @@
|
|
74 |
white-space: pre-wrap;
|
75 |
margin: 0;
|
76 |
line-height: 1.4;
|
|
|
77 |
}
|
78 |
|
79 |
.console-input {
|
@@ -86,12 +87,27 @@
|
|
86 |
}
|
87 |
|
88 |
/* Elements スタイル */
|
|
|
|
|
|
|
|
|
|
|
|
|
89 |
.dom-tree {
|
90 |
font-family: monospace;
|
|
|
|
|
|
|
|
|
91 |
}
|
92 |
|
93 |
.dom-node {
|
94 |
margin-left: 15px;
|
|
|
|
|
|
|
|
|
|
|
95 |
}
|
96 |
|
97 |
.dom-tag {
|
@@ -102,6 +118,65 @@
|
|
102 |
color: #ff7043;
|
103 |
}
|
104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
105 |
/* Storage スタイル */
|
106 |
.storage-table {
|
107 |
width: 100%;
|
@@ -129,9 +204,33 @@
|
|
129 |
padding: 2px 5px;
|
130 |
cursor: pointer;
|
131 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
132 |
`;
|
133 |
document.head.appendChild(style);
|
134 |
|
|
|
|
|
|
|
|
|
|
|
135 |
// 開発者ツールのUI構築
|
136 |
function createDevTools() {
|
137 |
const container = document.createElement('div');
|
@@ -179,6 +278,9 @@
|
|
179 |
|
180 |
document.body.appendChild(container);
|
181 |
|
|
|
|
|
|
|
182 |
// タブ切り替え機能
|
183 |
function createTab(name, panelId) {
|
184 |
const tab = document.createElement('div');
|
@@ -194,7 +296,112 @@
|
|
194 |
}
|
195 |
|
196 |
// 初期タブをアクティブに
|
197 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
198 |
}
|
199 |
|
200 |
// Consoleパネルの作成
|
@@ -254,11 +461,20 @@
|
|
254 |
panel.className = 'devtools-panel';
|
255 |
panel.id = 'elements-panel';
|
256 |
|
|
|
|
|
|
|
257 |
const tree = document.createElement('div');
|
258 |
tree.className = 'dom-tree';
|
259 |
tree.id = 'dom-tree';
|
260 |
|
261 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
262 |
|
263 |
// DOMツリーを構築
|
264 |
function buildDOMTree(node, parentElement, depth = 0) {
|
@@ -266,6 +482,38 @@
|
|
266 |
const element = document.createElement('div');
|
267 |
element.className = 'dom-node';
|
268 |
element.style.marginLeft = `${depth * 15}px`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
|
270 |
// タグ名
|
271 |
const tag = document.createElement('span');
|
@@ -308,12 +556,142 @@
|
|
308 |
}
|
309 |
}
|
310 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
311 |
// 初期DOMツリー構築
|
312 |
-
|
313 |
|
314 |
return panel;
|
315 |
}
|
316 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
317 |
// Storageパネルの作成
|
318 |
function createStoragePanel() {
|
319 |
const panel = document.createElement('div');
|
@@ -329,6 +707,19 @@
|
|
329 |
localStorageTable.className = 'storage-table';
|
330 |
panel.appendChild(localStorageTable);
|
331 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
332 |
// SessionStorage表示
|
333 |
const sessionStorageTitle = document.createElement('h3');
|
334 |
sessionStorageTitle.style.marginTop = '20px';
|
@@ -339,6 +730,19 @@
|
|
339 |
sessionStorageTable.className = 'storage-table';
|
340 |
panel.appendChild(sessionStorageTable);
|
341 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
// Cookie表示
|
343 |
const cookiesTitle = document.createElement('h3');
|
344 |
cookiesTitle.style.marginTop = '20px';
|
@@ -349,14 +753,27 @@
|
|
349 |
cookiesTable.className = 'storage-table';
|
350 |
panel.appendChild(cookiesTable);
|
351 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
352 |
// ストレージを表示する関数
|
353 |
function renderStorage() {
|
354 |
-
renderTable(localStorageTable, localStorage);
|
355 |
-
renderTable(sessionStorageTable, sessionStorage);
|
356 |
renderCookiesTable(cookiesTable);
|
357 |
}
|
358 |
|
359 |
-
function renderTable(tableElement, storage) {
|
360 |
tableElement.innerHTML = `
|
361 |
<thead>
|
362 |
<tr>
|
@@ -377,24 +794,34 @@
|
|
377 |
const row = document.createElement('tr');
|
378 |
|
379 |
const keyCell = document.createElement('td');
|
380 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
381 |
|
382 |
const valueCell = document.createElement('td');
|
383 |
-
|
384 |
-
|
385 |
-
|
386 |
-
|
387 |
-
|
388 |
-
const editBtn = document.createElement('button');
|
389 |
-
editBtn.className = 'storage-btn';
|
390 |
-
editBtn.textContent = 'Edit';
|
391 |
-
editBtn.onclick = () => {
|
392 |
-
const newValue = prompt('Enter new value:', value);
|
393 |
if (newValue !== null) {
|
394 |
storage.setItem(key, newValue);
|
395 |
renderStorage();
|
396 |
}
|
397 |
};
|
|
|
|
|
|
|
|
|
398 |
|
399 |
const deleteBtn = document.createElement('button');
|
400 |
deleteBtn.className = 'storage-btn';
|
@@ -404,7 +831,6 @@
|
|
404 |
renderStorage();
|
405 |
};
|
406 |
|
407 |
-
actionsCell.appendChild(editBtn);
|
408 |
actionsCell.appendChild(deleteBtn);
|
409 |
|
410 |
row.appendChild(keyCell);
|
@@ -433,15 +859,40 @@
|
|
433 |
if (!cookie.trim()) return;
|
434 |
|
435 |
const [name, ...valueParts] = cookie.split('=');
|
|
|
436 |
const value = valueParts.join('=').trim();
|
437 |
|
438 |
const row = document.createElement('tr');
|
439 |
|
440 |
const nameCell = document.createElement('td');
|
441 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
442 |
|
443 |
const valueCell = document.createElement('td');
|
444 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
445 |
|
446 |
const actionsCell = document.createElement('td');
|
447 |
actionsCell.className = 'storage-actions';
|
@@ -499,13 +950,12 @@
|
|
499 |
|
500 |
document.body.appendChild(button);
|
501 |
}
|
|
|
|
|
502 |
document.addEventListener('DOMContentLoaded', function() {
|
503 |
-
// 初期化
|
504 |
createDevTools();
|
505 |
createOpenButton();
|
506 |
-
|
507 |
-
// 初期メッセージ
|
508 |
console.log('開発者ツールが初期化されました');
|
509 |
console.log('このコンソールでJavaScriptを実行できます');
|
510 |
-
})
|
511 |
})();
|
|
|
66 |
}
|
67 |
|
68 |
.devtools-panel.active {
|
69 |
+
display: flex;
|
70 |
}
|
71 |
|
72 |
/* Console スタイル */
|
|
|
74 |
white-space: pre-wrap;
|
75 |
margin: 0;
|
76 |
line-height: 1.4;
|
77 |
+
flex: 1;
|
78 |
}
|
79 |
|
80 |
.console-input {
|
|
|
87 |
}
|
88 |
|
89 |
/* Elements スタイル */
|
90 |
+
.elements-container {
|
91 |
+
display: flex;
|
92 |
+
flex: 1;
|
93 |
+
overflow: hidden;
|
94 |
+
}
|
95 |
+
|
96 |
.dom-tree {
|
97 |
font-family: monospace;
|
98 |
+
flex: 1;
|
99 |
+
overflow: auto;
|
100 |
+
border-right: 1px solid #4fc3f7;
|
101 |
+
padding-right: 10px;
|
102 |
}
|
103 |
|
104 |
.dom-node {
|
105 |
margin-left: 15px;
|
106 |
+
position: relative;
|
107 |
+
}
|
108 |
+
|
109 |
+
.dom-node.selected {
|
110 |
+
background: rgba(79, 195, 247, 0.2);
|
111 |
}
|
112 |
|
113 |
.dom-tag {
|
|
|
118 |
color: #ff7043;
|
119 |
}
|
120 |
|
121 |
+
.css-panel {
|
122 |
+
flex: 1;
|
123 |
+
overflow: auto;
|
124 |
+
padding-left: 10px;
|
125 |
+
}
|
126 |
+
|
127 |
+
.css-rule {
|
128 |
+
margin-bottom: 15px;
|
129 |
+
border: 1px solid #4fc3f7;
|
130 |
+
padding: 8px;
|
131 |
+
}
|
132 |
+
|
133 |
+
.css-selector {
|
134 |
+
color: #4fc3f7;
|
135 |
+
margin-bottom: 5px;
|
136 |
+
}
|
137 |
+
|
138 |
+
.css-property {
|
139 |
+
display: flex;
|
140 |
+
margin-bottom: 3px;
|
141 |
+
}
|
142 |
+
|
143 |
+
.css-property-name {
|
144 |
+
color: #69f0ae;
|
145 |
+
min-width: 120px;
|
146 |
+
}
|
147 |
+
|
148 |
+
.css-property-value {
|
149 |
+
color: #e0e0e0;
|
150 |
+
flex: 1;
|
151 |
+
}
|
152 |
+
|
153 |
+
.css-toggle {
|
154 |
+
margin-left: 10px;
|
155 |
+
color: #ff7043;
|
156 |
+
cursor: pointer;
|
157 |
+
}
|
158 |
+
|
159 |
+
/* Context Menu */
|
160 |
+
.context-menu {
|
161 |
+
position: absolute;
|
162 |
+
background: #252a33;
|
163 |
+
border: 1px solid #4fc3f7;
|
164 |
+
z-index: 10000;
|
165 |
+
min-width: 200px;
|
166 |
+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
|
167 |
+
display: none;
|
168 |
+
}
|
169 |
+
|
170 |
+
.context-menu-item {
|
171 |
+
padding: 8px 15px;
|
172 |
+
cursor: pointer;
|
173 |
+
}
|
174 |
+
|
175 |
+
.context-menu-item:hover {
|
176 |
+
background: #4fc3f7;
|
177 |
+
color: #000;
|
178 |
+
}
|
179 |
+
|
180 |
/* Storage スタイル */
|
181 |
.storage-table {
|
182 |
width: 100%;
|
|
|
204 |
padding: 2px 5px;
|
205 |
cursor: pointer;
|
206 |
}
|
207 |
+
|
208 |
+
.editable {
|
209 |
+
cursor: pointer;
|
210 |
+
padding: 2px 5px;
|
211 |
+
border: 1px dashed transparent;
|
212 |
+
}
|
213 |
+
|
214 |
+
.editable:hover {
|
215 |
+
border-color: #4fc3f7;
|
216 |
+
}
|
217 |
+
|
218 |
+
.add-btn {
|
219 |
+
background: #69f0ae;
|
220 |
+
color: #000;
|
221 |
+
border: none;
|
222 |
+
padding: 5px 10px;
|
223 |
+
margin-top: 10px;
|
224 |
+
cursor: pointer;
|
225 |
+
}
|
226 |
`;
|
227 |
document.head.appendChild(style);
|
228 |
|
229 |
+
// コンテキストメニュー用の変数
|
230 |
+
let contextMenu = null;
|
231 |
+
let selectedElement = null;
|
232 |
+
let selectedDOMNode = null;
|
233 |
+
|
234 |
// 開発者ツールのUI構築
|
235 |
function createDevTools() {
|
236 |
const container = document.createElement('div');
|
|
|
278 |
|
279 |
document.body.appendChild(container);
|
280 |
|
281 |
+
// コンテキストメニュー作成
|
282 |
+
createContextMenu();
|
283 |
+
|
284 |
// タブ切り替え機能
|
285 |
function createTab(name, panelId) {
|
286 |
const tab = document.createElement('div');
|
|
|
296 |
}
|
297 |
|
298 |
// 初期タブをアクティブに
|
299 |
+
elementsTab.click();
|
300 |
+
}
|
301 |
+
|
302 |
+
// コンテキストメニュー作成
|
303 |
+
function createContextMenu() {
|
304 |
+
contextMenu = document.createElement('div');
|
305 |
+
contextMenu.className = 'context-menu';
|
306 |
+
contextMenu.innerHTML = `
|
307 |
+
<div class="context-menu-item" data-action="edit-html">HTMLとして編集</div>
|
308 |
+
<div class="context-menu-item" data-action="add-attribute">属性を追加</div>
|
309 |
+
<div class="context-menu-item" data-action="edit-element">要素を編集</div>
|
310 |
+
<div class="context-menu-item" data-action="duplicate">要素を複製</div>
|
311 |
+
<div class="context-menu-item" data-action="remove">要素を削除</div>
|
312 |
+
<div class="context-menu-item" data-action="toggle-visibility">要素を非表示</div>
|
313 |
+
<div class="context-menu-item" data-action="force-state">状態を強制</div>
|
314 |
+
`;
|
315 |
+
document.body.appendChild(contextMenu);
|
316 |
+
|
317 |
+
// メニューアイテムのクリック処理
|
318 |
+
contextMenu.querySelectorAll('.context-menu-item').forEach(item => {
|
319 |
+
item.addEventListener('click', (e) => {
|
320 |
+
const action = e.target.getAttribute('data-action');
|
321 |
+
handleContextMenuAction(action);
|
322 |
+
contextMenu.style.display = 'none';
|
323 |
+
});
|
324 |
+
});
|
325 |
+
|
326 |
+
// メニュー外をクリックで閉じる
|
327 |
+
document.addEventListener('click', (e) => {
|
328 |
+
if (e.target !== contextMenu && !contextMenu.contains(e.target)) {
|
329 |
+
contextMenu.style.display = 'none';
|
330 |
+
}
|
331 |
+
});
|
332 |
+
}
|
333 |
+
|
334 |
+
// コンテキストメニューのアクション処理
|
335 |
+
function handleContextMenuAction(action) {
|
336 |
+
if (!selectedElement) return;
|
337 |
+
|
338 |
+
switch (action) {
|
339 |
+
case 'edit-html':
|
340 |
+
const html = prompt('HTMLを編集', selectedElement.outerHTML);
|
341 |
+
if (html) {
|
342 |
+
selectedElement.outerHTML = html;
|
343 |
+
refreshElementsPanel();
|
344 |
+
}
|
345 |
+
break;
|
346 |
+
|
347 |
+
case 'add-attribute':
|
348 |
+
const attrName = prompt('属性名を入力');
|
349 |
+
if (attrName) {
|
350 |
+
const attrValue = prompt('属性値を入力');
|
351 |
+
selectedElement.setAttribute(attrName, attrValue || '');
|
352 |
+
refreshElementsPanel();
|
353 |
+
}
|
354 |
+
break;
|
355 |
+
|
356 |
+
case 'edit-element':
|
357 |
+
const tagName = prompt('タグ名を編集', selectedElement.tagName.toLowerCase());
|
358 |
+
if (tagName) {
|
359 |
+
const newElement = document.createElement(tagName);
|
360 |
+
Array.from(selectedElement.attributes).forEach(attr => {
|
361 |
+
newElement.setAttribute(attr.name, attr.value);
|
362 |
+
});
|
363 |
+
newElement.innerHTML = selectedElement.innerHTML;
|
364 |
+
selectedElement.parentNode.replaceChild(newElement, selectedElement);
|
365 |
+
selectedElement = newElement;
|
366 |
+
refreshElementsPanel();
|
367 |
+
}
|
368 |
+
break;
|
369 |
+
|
370 |
+
case 'duplicate':
|
371 |
+
const clone = selectedElement.cloneNode(true);
|
372 |
+
selectedElement.parentNode.insertBefore(clone, selectedElement.nextSibling);
|
373 |
+
refreshElementsPanel();
|
374 |
+
break;
|
375 |
+
|
376 |
+
case 'remove':
|
377 |
+
if (confirm('要素を削除しますか?')) {
|
378 |
+
selectedElement.parentNode.removeChild(selectedElement);
|
379 |
+
refreshElementsPanel();
|
380 |
+
}
|
381 |
+
break;
|
382 |
+
|
383 |
+
case 'toggle-visibility':
|
384 |
+
if (selectedElement.style.display === 'none') {
|
385 |
+
selectedElement.style.display = '';
|
386 |
+
} else {
|
387 |
+
selectedElement.style.display = 'none';
|
388 |
+
}
|
389 |
+
refreshElementsPanel();
|
390 |
+
break;
|
391 |
+
|
392 |
+
case 'force-state':
|
393 |
+
const state = prompt('強制する状態を入力 (例: hover, active, focus)', 'hover');
|
394 |
+
if (state) {
|
395 |
+
// 既存の状態クラスを削除
|
396 |
+
selectedElement.classList.remove('force-hover', 'force-active', 'force-focus',
|
397 |
+
'force-focus-within', 'force-focus-visible', 'force-target');
|
398 |
+
|
399 |
+
// 新しい状態クラスを追加
|
400 |
+
selectedElement.classList.add(`force-${state}`);
|
401 |
+
refreshElementsPanel();
|
402 |
+
}
|
403 |
+
break;
|
404 |
+
}
|
405 |
}
|
406 |
|
407 |
// Consoleパネルの作成
|
|
|
461 |
panel.className = 'devtools-panel';
|
462 |
panel.id = 'elements-panel';
|
463 |
|
464 |
+
const container = document.createElement('div');
|
465 |
+
container.className = 'elements-container';
|
466 |
+
|
467 |
const tree = document.createElement('div');
|
468 |
tree.className = 'dom-tree';
|
469 |
tree.id = 'dom-tree';
|
470 |
|
471 |
+
const cssPanel = document.createElement('div');
|
472 |
+
cssPanel.className = 'css-panel';
|
473 |
+
cssPanel.id = 'css-panel';
|
474 |
+
|
475 |
+
container.appendChild(tree);
|
476 |
+
container.appendChild(cssPanel);
|
477 |
+
panel.appendChild(container);
|
478 |
|
479 |
// DOMツリーを構築
|
480 |
function buildDOMTree(node, parentElement, depth = 0) {
|
|
|
482 |
const element = document.createElement('div');
|
483 |
element.className = 'dom-node';
|
484 |
element.style.marginLeft = `${depth * 15}px`;
|
485 |
+
element.dataset.elementId = node.id || Math.random().toString(36).substr(2, 9);
|
486 |
+
|
487 |
+
// 右クリックイベント
|
488 |
+
element.oncontextmenu = (e) => {
|
489 |
+
e.preventDefault();
|
490 |
+
selectedElement = node;
|
491 |
+
selectedDOMNode = element;
|
492 |
+
|
493 |
+
// 選択状態を更新
|
494 |
+
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
495 |
+
element.classList.add('selected');
|
496 |
+
|
497 |
+
// コンテキストメニュー表示
|
498 |
+
contextMenu.style.display = 'block';
|
499 |
+
contextMenu.style.left = `${e.pageX}px`;
|
500 |
+
contextMenu.style.top = `${e.pageY}px`;
|
501 |
+
|
502 |
+
// CSSパネルを更新
|
503 |
+
updateCSSPanel(node);
|
504 |
+
};
|
505 |
+
|
506 |
+
// クリックで選択
|
507 |
+
element.onclick = (e) => {
|
508 |
+
e.stopPropagation();
|
509 |
+
selectedElement = node;
|
510 |
+
selectedDOMNode = element;
|
511 |
+
|
512 |
+
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
513 |
+
element.classList.add('selected');
|
514 |
+
|
515 |
+
updateCSSPanel(node);
|
516 |
+
};
|
517 |
|
518 |
// タグ名
|
519 |
const tag = document.createElement('span');
|
|
|
556 |
}
|
557 |
}
|
558 |
|
559 |
+
// CSSパネルを更新
|
560 |
+
function updateCSSPanel(element) {
|
561 |
+
const cssPanel = document.getElementById('css-panel');
|
562 |
+
cssPanel.innerHTML = '';
|
563 |
+
|
564 |
+
if (!element) return;
|
565 |
+
|
566 |
+
// インラインスタイル
|
567 |
+
if (element.style.length > 0) {
|
568 |
+
const inlineRule = document.createElement('div');
|
569 |
+
inlineRule.className = 'css-rule';
|
570 |
+
|
571 |
+
const selector = document.createElement('div');
|
572 |
+
selector.className = 'css-selector';
|
573 |
+
selector.textContent = 'インラインスタイル';
|
574 |
+
inlineRule.appendChild(selector);
|
575 |
+
|
576 |
+
for (let i = 0; i < element.style.length; i++) {
|
577 |
+
const propName = element.style[i];
|
578 |
+
const propValue = element.style[propName];
|
579 |
+
|
580 |
+
const propDiv = document.createElement('div');
|
581 |
+
propDiv.className = 'css-property';
|
582 |
+
|
583 |
+
const nameSpan = document.createElement('span');
|
584 |
+
nameSpan.className = 'css-property-name editable';
|
585 |
+
nameSpan.textContent = propName;
|
586 |
+
nameSpan.onclick = () => editCSSProperty(element, propName, 'style');
|
587 |
+
|
588 |
+
const valueSpan = document.createElement('span');
|
589 |
+
valueSpan.className = 'css-property-value editable';
|
590 |
+
valueSpan.textContent = propValue;
|
591 |
+
valueSpan.onclick = () => editCSSProperty(element, propName, 'style');
|
592 |
+
|
593 |
+
const toggleSpan = document.createElement('span');
|
594 |
+
toggleSpan.className = 'css-toggle';
|
595 |
+
toggleSpan.textContent = '×';
|
596 |
+
toggleSpan.title = 'プロパティを無効化';
|
597 |
+
toggleSpan.onclick = () => {
|
598 |
+
element.style[propName] = '';
|
599 |
+
updateCSSPanel(element);
|
600 |
+
};
|
601 |
+
|
602 |
+
propDiv.appendChild(nameSpan);
|
603 |
+
propDiv.appendChild(valueSpan);
|
604 |
+
propDiv.appendChild(toggleSpan);
|
605 |
+
inlineRule.appendChild(propDiv);
|
606 |
+
}
|
607 |
+
|
608 |
+
cssPanel.appendChild(inlineRule);
|
609 |
+
}
|
610 |
+
|
611 |
+
// 計算されたスタイル
|
612 |
+
const computedStyles = window.getComputedStyle(element);
|
613 |
+
const computedRule = document.createElement('div');
|
614 |
+
computedRule.className = 'css-rule';
|
615 |
+
|
616 |
+
const computedSelector = document.createElement('div');
|
617 |
+
computedSelector.className = 'css-selector';
|
618 |
+
computedSelector.textContent = '計算されたスタイル';
|
619 |
+
computedRule.appendChild(computedSelector);
|
620 |
+
|
621 |
+
// 重要なプロパティのみ表示
|
622 |
+
const importantProps = [
|
623 |
+
'display', 'position', 'width', 'height', 'margin', 'padding',
|
624 |
+
'color', 'background', 'border', 'font', 'flex', 'grid'
|
625 |
+
];
|
626 |
+
|
627 |
+
importantProps.forEach(prop => {
|
628 |
+
const value = computedStyles[prop];
|
629 |
+
|
630 |
+
const propDiv = document.createElement('div');
|
631 |
+
propDiv.className = 'css-property';
|
632 |
+
|
633 |
+
const nameSpan = document.createElement('span');
|
634 |
+
nameSpan.className = 'css-property-name';
|
635 |
+
nameSpan.textContent = prop;
|
636 |
+
|
637 |
+
const valueSpan = document.createElement('span');
|
638 |
+
valueSpan.className = 'css-property-value';
|
639 |
+
valueSpan.textContent = value;
|
640 |
+
|
641 |
+
propDiv.appendChild(nameSpan);
|
642 |
+
propDiv.appendChild(valueSpan);
|
643 |
+
computedRule.appendChild(propDiv);
|
644 |
+
});
|
645 |
+
|
646 |
+
cssPanel.appendChild(computedRule);
|
647 |
+
}
|
648 |
+
|
649 |
+
// CSSプロパティを編集
|
650 |
+
function editCSSProperty(element, propName, styleType) {
|
651 |
+
let currentValue = '';
|
652 |
+
|
653 |
+
if (styleType === 'style') {
|
654 |
+
currentValue = element.style[propName];
|
655 |
+
}
|
656 |
+
|
657 |
+
const newValue = prompt(`${propName} の新しい値を入力`, currentValue);
|
658 |
+
|
659 |
+
if (newValue !== null) {
|
660 |
+
if (styleType === 'style') {
|
661 |
+
element.style[propName] = newValue;
|
662 |
+
}
|
663 |
+
|
664 |
+
updateCSSPanel(element);
|
665 |
+
refreshElementsPanel();
|
666 |
+
}
|
667 |
+
}
|
668 |
+
|
669 |
// 初期DOMツリー構築
|
670 |
+
refreshElementsPanel();
|
671 |
|
672 |
return panel;
|
673 |
}
|
674 |
|
675 |
+
// Elementsパネルをリフレッシュ
|
676 |
+
function refreshElementsPanel() {
|
677 |
+
const tree = document.getElementById('dom-tree');
|
678 |
+
tree.innerHTML = '';
|
679 |
+
buildDOMTree(document.documentElement, tree);
|
680 |
+
|
681 |
+
if (selectedElement) {
|
682 |
+
const elementId = selectedElement.id || Array.from(selectedElement.attributes)
|
683 |
+
.find(attr => attr.name.startsWith('data-element-id'))?.value;
|
684 |
+
|
685 |
+
if (elementId) {
|
686 |
+
const node = document.querySelector(`[data-element-id="${elementId}"]`);
|
687 |
+
if (node) {
|
688 |
+
node.classList.add('selected');
|
689 |
+
updateCSSPanel(selectedElement);
|
690 |
+
}
|
691 |
+
}
|
692 |
+
}
|
693 |
+
}
|
694 |
+
|
695 |
// Storageパネルの作成
|
696 |
function createStoragePanel() {
|
697 |
const panel = document.createElement('div');
|
|
|
707 |
localStorageTable.className = 'storage-table';
|
708 |
panel.appendChild(localStorageTable);
|
709 |
|
710 |
+
const addLocalStorageBtn = document.createElement('button');
|
711 |
+
addLocalStorageBtn.className = 'add-btn';
|
712 |
+
addLocalStorageBtn.textContent = '+ Local Storageに追加';
|
713 |
+
addLocalStorageBtn.onclick = () => {
|
714 |
+
const key = prompt('キー名を入力');
|
715 |
+
if (key) {
|
716 |
+
const value = prompt('値を入力');
|
717 |
+
localStorage.setItem(key, value);
|
718 |
+
renderStorage();
|
719 |
+
}
|
720 |
+
};
|
721 |
+
panel.appendChild(addLocalStorageBtn);
|
722 |
+
|
723 |
// SessionStorage表示
|
724 |
const sessionStorageTitle = document.createElement('h3');
|
725 |
sessionStorageTitle.style.marginTop = '20px';
|
|
|
730 |
sessionStorageTable.className = 'storage-table';
|
731 |
panel.appendChild(sessionStorageTable);
|
732 |
|
733 |
+
const addSessionStorageBtn = document.createElement('button');
|
734 |
+
addSessionStorageBtn.className = 'add-btn';
|
735 |
+
addSessionStorageBtn.textContent = '+ Session Storageに追加';
|
736 |
+
addSessionStorageBtn.onclick = () => {
|
737 |
+
const key = prompt('キー名を入力');
|
738 |
+
if (key) {
|
739 |
+
const value = prompt('値を入力');
|
740 |
+
sessionStorage.setItem(key, value);
|
741 |
+
renderStorage();
|
742 |
+
}
|
743 |
+
};
|
744 |
+
panel.appendChild(addSessionStorageBtn);
|
745 |
+
|
746 |
// Cookie表示
|
747 |
const cookiesTitle = document.createElement('h3');
|
748 |
cookiesTitle.style.marginTop = '20px';
|
|
|
753 |
cookiesTable.className = 'storage-table';
|
754 |
panel.appendChild(cookiesTable);
|
755 |
|
756 |
+
const addCookieBtn = document.createElement('button');
|
757 |
+
addCookieBtn.className = 'add-btn';
|
758 |
+
addCookieBtn.textContent = '+ Cookieに追加';
|
759 |
+
addCookieBtn.onclick = () => {
|
760 |
+
const name = prompt('Cookie名を入力');
|
761 |
+
if (name) {
|
762 |
+
const value = prompt('値を入力');
|
763 |
+
document.cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}; path=/`;
|
764 |
+
renderStorage();
|
765 |
+
}
|
766 |
+
};
|
767 |
+
panel.appendChild(addCookieBtn);
|
768 |
+
|
769 |
// ストレージを表示する関数
|
770 |
function renderStorage() {
|
771 |
+
renderTable(localStorageTable, localStorage, 'local');
|
772 |
+
renderTable(sessionStorageTable, sessionStorage, 'session');
|
773 |
renderCookiesTable(cookiesTable);
|
774 |
}
|
775 |
|
776 |
+
function renderTable(tableElement, storage, type) {
|
777 |
tableElement.innerHTML = `
|
778 |
<thead>
|
779 |
<tr>
|
|
|
794 |
const row = document.createElement('tr');
|
795 |
|
796 |
const keyCell = document.createElement('td');
|
797 |
+
const keySpan = document.createElement('span');
|
798 |
+
keySpan.className = 'editable';
|
799 |
+
keySpan.textContent = key;
|
800 |
+
keySpan.onclick = () => {
|
801 |
+
const newKey = prompt('新しいキー名を入力', key);
|
802 |
+
if (newKey && newKey !== key) {
|
803 |
+
storage.setItem(newKey, value);
|
804 |
+
storage.removeItem(key);
|
805 |
+
renderStorage();
|
806 |
+
}
|
807 |
+
};
|
808 |
+
keyCell.appendChild(keySpan);
|
809 |
|
810 |
const valueCell = document.createElement('td');
|
811 |
+
const valueSpan = document.createElement('span');
|
812 |
+
valueSpan.className = 'editable';
|
813 |
+
valueSpan.textContent = value;
|
814 |
+
valueSpan.onclick = () => {
|
815 |
+
const newValue = prompt('新しい値を入力', value);
|
|
|
|
|
|
|
|
|
|
|
816 |
if (newValue !== null) {
|
817 |
storage.setItem(key, newValue);
|
818 |
renderStorage();
|
819 |
}
|
820 |
};
|
821 |
+
valueCell.appendChild(valueSpan);
|
822 |
+
|
823 |
+
const actionsCell = document.createElement('td');
|
824 |
+
actionsCell.className = 'storage-actions';
|
825 |
|
826 |
const deleteBtn = document.createElement('button');
|
827 |
deleteBtn.className = 'storage-btn';
|
|
|
831 |
renderStorage();
|
832 |
};
|
833 |
|
|
|
834 |
actionsCell.appendChild(deleteBtn);
|
835 |
|
836 |
row.appendChild(keyCell);
|
|
|
859 |
if (!cookie.trim()) return;
|
860 |
|
861 |
const [name, ...valueParts] = cookie.split('=');
|
862 |
+
const decodedName = decodeURIComponent(name.trim());
|
863 |
const value = valueParts.join('=').trim();
|
864 |
|
865 |
const row = document.createElement('tr');
|
866 |
|
867 |
const nameCell = document.createElement('td');
|
868 |
+
const nameSpan = document.createElement('span');
|
869 |
+
nameSpan.className = 'editable';
|
870 |
+
nameSpan.textContent = decodedName;
|
871 |
+
nameSpan.onclick = () => {
|
872 |
+
const newName = prompt('新しい名前を入力', decodedName);
|
873 |
+
if (newName && newName !== decodedName) {
|
874 |
+
const newValue = prompt('新しい値を入力', decodeURIComponent(value));
|
875 |
+
if (newValue !== null) {
|
876 |
+
document.cookie = `${encodeURIComponent(newName)}=${encodeURIComponent(newValue)}; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/`;
|
877 |
+
document.cookie = `${name.trim()}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
|
878 |
+
renderStorage();
|
879 |
+
}
|
880 |
+
}
|
881 |
+
};
|
882 |
+
nameCell.appendChild(nameSpan);
|
883 |
|
884 |
const valueCell = document.createElement('td');
|
885 |
+
const valueSpan = document.createElement('span');
|
886 |
+
valueSpan.className = 'editable';
|
887 |
+
valueSpan.textContent = decodeURIComponent(value);
|
888 |
+
valueSpan.onclick = () => {
|
889 |
+
const newValue = prompt('新しい値を入力', decodeURIComponent(value));
|
890 |
+
if (newValue !== null) {
|
891 |
+
document.cookie = `${name.trim()}=${encodeURIComponent(newValue)}; expires=Fri, 31 Dec 9999 23:59:59 GMT; path=/`;
|
892 |
+
renderStorage();
|
893 |
+
}
|
894 |
+
};
|
895 |
+
valueCell.appendChild(valueSpan);
|
896 |
|
897 |
const actionsCell = document.createElement('td');
|
898 |
actionsCell.className = 'storage-actions';
|
|
|
950 |
|
951 |
document.body.appendChild(button);
|
952 |
}
|
953 |
+
|
954 |
+
// 初期化
|
955 |
document.addEventListener('DOMContentLoaded', function() {
|
|
|
956 |
createDevTools();
|
957 |
createOpenButton();
|
|
|
|
|
958 |
console.log('開発者ツールが初期化されました');
|
959 |
console.log('このコンソールでJavaScriptを実行できます');
|
960 |
+
});
|
961 |
})();
|