dev-tools / dev-tools.js
soiz1's picture
Update dev-tools.js
bb2bd2a verified
raw
history blame
14.5 kB
(function() {
// スタイルの動的追加
const style = document.createElement('style');
style.textContent = `
.devtools-container {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 300px;
background: #1a1e24;
color: #e0e0e0;
font-family: 'Consolas', 'Courier New', monospace;
border-top: 2px solid #4fc3f7;
display: flex;
flex-direction: column;
z-index: 9999;
box-shadow: 0 -5px 15px rgba(0, 0, 0, 0.5);
}
.devtools-header {
background: #252a33;
padding: 8px 15px;
display: flex;
justify-content: space-between;
border-bottom: 1px solid #4fc3f7;
}
.devtools-tabs {
display: flex;
gap: 10px;
}
.devtools-tab {
padding: 5px 10px;
background: #2c313a;
border: 1px solid #4fc3f7;
border-radius: 3px 3px 0 0;
cursor: pointer;
transition: all 0.2s;
}
.devtools-tab.active {
background: #4fc3f7;
color: #000;
}
.devtools-close {
background: transparent;
border: none;
color: #e0e0e0;
cursor: pointer;
}
.devtools-content {
flex: 1;
display: flex;
overflow: hidden;
}
.devtools-panel {
flex: 1;
padding: 10px;
overflow: auto;
display: none;
}
.devtools-panel.active {
display: block;
}
/* Console スタイル */
#console-log {
white-space: pre-wrap;
margin: 0;
line-height: 1.4;
}
.console-input {
width: 100%;
background: #252a33;
border: 1px solid #4fc3f7;
color: #e0e0e0;
padding: 8px;
margin-top: 10px;
}
/* Elements スタイル */
.dom-tree {
font-family: monospace;
}
.dom-node {
margin-left: 15px;
}
.dom-tag {
color: #4fc3f7;
}
.dom-attr {
color: #ff7043;
}
/* Storage スタイル */
.storage-table {
width: 100%;
border-collapse: collapse;
}
.storage-table th, .storage-table td {
border: 1px solid #4fc3f7;
padding: 5px;
text-align: left;
}
.storage-table th {
background: #252a33;
}
.storage-actions {
display: flex;
gap: 5px;
}
.storage-btn {
background: #4fc3f7;
border: none;
padding: 2px 5px;
cursor: pointer;
}
`;
document.head.appendChild(style);
// 開発者ツールのUI構築
function createDevTools() {
const container = document.createElement('div');
container.className = 'devtools-container';
container.id = 'devtools-container';
container.style.display = 'none';
// ヘッダー部分
const header = document.createElement('div');
header.className = 'devtools-header';
const tabs = document.createElement('div');
tabs.className = 'devtools-tabs';
const consoleTab = createTab('Console', 'console');
const elementsTab = createTab('Elements', 'elements');
const storageTab = createTab('Storage', 'storage');
tabs.appendChild(consoleTab);
tabs.appendChild(elementsTab);
tabs.appendChild(storageTab);
const closeBtn = document.createElement('button');
closeBtn.className = 'devtools-close';
closeBtn.textContent = '×';
closeBtn.onclick = toggleDevTools;
header.appendChild(tabs);
header.appendChild(closeBtn);
// コンテンツ部分
const content = document.createElement('div');
content.className = 'devtools-content';
const consolePanel = createConsolePanel();
const elementsPanel = createElementsPanel();
const storagePanel = createStoragePanel();
content.appendChild(consolePanel);
content.appendChild(elementsPanel);
content.appendChild(storagePanel);
container.appendChild(header);
container.appendChild(content);
document.body.appendChild(container);
// タブ切り替え機能
function createTab(name, panelId) {
const tab = document.createElement('div');
tab.className = 'devtools-tab';
tab.textContent = name;
tab.onclick = () => {
document.querySelectorAll('.devtools-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.devtools-panel').forEach(p => p.classList.remove('active'));
tab.classList.add('active');
document.getElementById(panelId + '-panel').classList.add('active');
};
return tab;
}
// 初期タブをアクティブに
consoleTab.click();
}
// Consoleパネルの作成
function createConsolePanel() {
const panel = document.createElement('div');
panel.className = 'devtools-panel';
panel.id = 'console-panel';
const log = document.createElement('div');
log.id = 'console-log';
const input = document.createElement('input');
input.className = 'console-input';
input.placeholder = 'ここにJavaScriptを入力... (Enterで実行)';
input.onkeypress = (e) => {
if (e.key === 'Enter') {
try {
const result = eval(e.target.value);
if (result !== undefined) {
logMessage('> ' + e.target.value, '#4fc3f7');
logMessage('← ' + result, '#69f0ae');
}
} catch (err) {
logMessage(err.message, '#ff5252');
}
e.target.value = '';
}
};
panel.appendChild(log);
panel.appendChild(input);
// コンソールログをキャプチャ
['log', 'error', 'warn'].forEach(method => {
const original = console[method];
console[method] = (...args) => {
original.apply(console, args);
const color = method === 'error' ? '#ff5252' : method === 'warn' ? '#ffab40' : '#e0e0e0';
logMessage(args.join(' '), color);
};
});
function logMessage(message, color) {
const line = document.createElement('div');
line.style.color = color;
line.textContent = message;
log.appendChild(line);
log.scrollTop = log.scrollHeight;
}
return panel;
}
// Elementsパネルの作成
function createElementsPanel() {
const panel = document.createElement('div');
panel.className = 'devtools-panel';
panel.id = 'elements-panel';
const tree = document.createElement('div');
tree.className = 'dom-tree';
tree.id = 'dom-tree';
panel.appendChild(tree);
// DOMツリーを構築
function buildDOMTree(node, parentElement, depth = 0) {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = document.createElement('div');
element.className = 'dom-node';
element.style.marginLeft = `${depth * 15}px`;
// タグ名
const tag = document.createElement('span');
tag.className = 'dom-tag';
tag.textContent = `<${node.tagName.toLowerCase()}`;
element.appendChild(tag);
// 属性
Array.from(node.attributes).forEach(attr => {
const attrSpan = document.createElement('span');
attrSpan.className = 'dom-attr';
attrSpan.textContent = ` ${attr.name}="${attr.value}"`;
element.appendChild(attrSpan);
});
element.appendChild(document.createTextNode('>'));
// 子要素
if (node.childNodes.length > 0) {
node.childNodes.forEach(child => {
buildDOMTree(child, element, depth + 1);
});
}
// 閉じタグ
if (node.childNodes.length > 0 || node.tagName.toLowerCase() !== 'br') {
const closeTag = document.createElement('div');
closeTag.style.marginLeft = `${depth * 15}px`;
closeTag.innerHTML = `<span class="dom-tag">&lt;/${node.tagName.toLowerCase()}&gt;</span>`;
element.appendChild(closeTag);
}
parentElement.appendChild(element);
} else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
const text = document.createElement('div');
text.style.marginLeft = `${depth * 15}px`;
text.style.color = '#e0e0e0';
text.textContent = `"${node.textContent.trim()}"`;
parentElement.appendChild(text);
}
}
// 初期DOMツリー構築
buildDOMTree(document.documentElement, tree);
return panel;
}
// Storageパネルの作成
function createStoragePanel() {
const panel = document.createElement('div');
panel.className = 'devtools-panel';
panel.id = 'storage-panel';
// LocalStorage表示
const localStorageTitle = document.createElement('h3');
localStorageTitle.textContent = 'Local Storage';
panel.appendChild(localStorageTitle);
const localStorageTable = document.createElement('table');
localStorageTable.className = 'storage-table';
panel.appendChild(localStorageTable);
// SessionStorage表示
const sessionStorageTitle = document.createElement('h3');
sessionStorageTitle.style.marginTop = '20px';
sessionStorageTitle.textContent = 'Session Storage';
panel.appendChild(sessionStorageTitle);
const sessionStorageTable = document.createElement('table');
sessionStorageTable.className = 'storage-table';
panel.appendChild(sessionStorageTable);
// Cookie表示
const cookiesTitle = document.createElement('h3');
cookiesTitle.style.marginTop = '20px';
cookiesTitle.textContent = 'Cookies';
panel.appendChild(cookiesTitle);
const cookiesTable = document.createElement('table');
cookiesTable.className = 'storage-table';
panel.appendChild(cookiesTable);
// ストレージを表示する関数
function renderStorage() {
renderTable(localStorageTable, localStorage);
renderTable(sessionStorageTable, sessionStorage);
renderCookiesTable(cookiesTable);
}
function renderTable(tableElement, storage) {
tableElement.innerHTML = `
<thead>
<tr>
<th>Key</th>
<th>Value</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
`;
const tbody = tableElement.querySelector('tbody');
for (let i = 0; i < storage.length; i++) {
const key = storage.key(i);
const value = storage.getItem(key);
const row = document.createElement('tr');
const keyCell = document.createElement('td');
keyCell.textContent = key;
const valueCell = document.createElement('td');
valueCell.textContent = value;
const actionsCell = document.createElement('td');
actionsCell.className = 'storage-actions';
const editBtn = document.createElement('button');
editBtn.className = 'storage-btn';
editBtn.textContent = 'Edit';
editBtn.onclick = () => {
const newValue = prompt('Enter new value:', value);
if (newValue !== null) {
storage.setItem(key, newValue);
renderStorage();
}
};
const deleteBtn = document.createElement('button');
deleteBtn.className = 'storage-btn';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => {
storage.removeItem(key);
renderStorage();
};
actionsCell.appendChild(editBtn);
actionsCell.appendChild(deleteBtn);
row.appendChild(keyCell);
row.appendChild(valueCell);
row.appendChild(actionsCell);
tbody.appendChild(row);
}
}
function renderCookiesTable(tableElement) {
tableElement.innerHTML = `
<thead>
<tr>
<th>Name</th>
<th>Value</th>
<th>Actions</th>
</tr>
</thead>
<tbody></tbody>
`;
const tbody = tableElement.querySelector('tbody');
document.cookie.split(';').forEach(cookie => {
if (!cookie.trim()) return;
const [name, ...valueParts] = cookie.split('=');
const value = valueParts.join('=').trim();
const row = document.createElement('tr');
const nameCell = document.createElement('td');
nameCell.textContent = name.trim();
const valueCell = document.createElement('td');
valueCell.textContent = decodeURIComponent(value);
const actionsCell = document.createElement('td');
actionsCell.className = 'storage-actions';
const deleteBtn = document.createElement('button');
deleteBtn.className = 'storage-btn';
deleteBtn.textContent = 'Delete';
deleteBtn.onclick = () => {
document.cookie = `${name.trim()}=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/`;
renderStorage();
};
actionsCell.appendChild(deleteBtn);
row.appendChild(nameCell);
row.appendChild(valueCell);
row.appendChild(actionsCell);
tbody.appendChild(row);
});
}
// 初期表示
renderStorage();
return panel;
}
// 開発者ツールの表示/非表示切り替え
function toggleDevTools() {
const container = document.getElementById('devtools-container');
if (container.style.display === 'none') {
container.style.display = 'flex';
} else {
container.style.display = 'none';
}
}
// 開発者ツールを開くボタンの作成
function createOpenButton() {
const button = document.createElement('button');
button.id = 'open-devtools-btn';
button.textContent = '開発者ツールを開く';
button.style.position = 'fixed';
button.style.bottom = '10px';
button.style.right = '10px';
button.style.padding = '8px 16px';
button.style.background = '#4fc3f7';
button.style.color = '#000';
button.style.border = 'none';
button.style.borderRadius = '4px';
button.style.cursor = 'pointer';
button.style.zIndex = '9998';
button.onclick = toggleDevTools;
document.body.appendChild(button);
}
document.addEventListener('DOMContentLoaded', function() {
// 初期化
createDevTools();
createOpenButton();
// 初期メッセージ
console.log('開発者ツールが初期化されました');
console.log('このコンソールでJavaScriptを実行できます');
}
})();