Update dev-tools.js
Browse files- dev-tools.js +287 -0
dev-tools.js
CHANGED
@@ -1731,7 +1731,294 @@ function refreshElementsPanel() {
|
|
1731 |
}
|
1732 |
}
|
1733 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1734 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1735 |
// 開発者ツール表示/非表示
|
1736 |
function toggleDevTools() {
|
1737 |
const container = document.getElementById('devtools-container');
|
|
|
1731 |
}
|
1732 |
}
|
1733 |
}
|
1734 |
+
// Elementsパネル作成
|
1735 |
+
function createElementsPanel() {
|
1736 |
+
const panel = document.createElement('div');
|
1737 |
+
panel.className = 'devtools-panel';
|
1738 |
+
panel.id = 'elements-panel';
|
1739 |
+
|
1740 |
+
const container = document.createElement('div');
|
1741 |
+
container.className = 'elements-container';
|
1742 |
+
|
1743 |
+
const tree = document.createElement('div');
|
1744 |
+
tree.className = 'dom-tree';
|
1745 |
+
tree.id = 'dom-tree';
|
1746 |
+
|
1747 |
+
const cssPanel = document.createElement('div');
|
1748 |
+
cssPanel.className = 'css-panel';
|
1749 |
+
cssPanel.id = 'css-panel';
|
1750 |
+
|
1751 |
+
container.appendChild(tree);
|
1752 |
+
container.appendChild(cssPanel);
|
1753 |
+
panel.appendChild(container);
|
1754 |
+
|
1755 |
+
// CSSパネル更新関数
|
1756 |
+
function updateCSSPanel(element) {
|
1757 |
+
const cssPanel = document.getElementById('css-panel');
|
1758 |
+
cssPanel.innerHTML = '';
|
1759 |
+
|
1760 |
+
if (!element) return;
|
1761 |
+
|
1762 |
+
if (element.style.length > 0) {
|
1763 |
+
const inlineRule = document.createElement('div');
|
1764 |
+
inlineRule.className = 'css-rule';
|
1765 |
+
|
1766 |
+
const selector = document.createElement('div');
|
1767 |
+
selector.className = 'css-selector';
|
1768 |
+
selector.textContent = 'インラインスタイル';
|
1769 |
+
inlineRule.appendChild(selector);
|
1770 |
+
|
1771 |
+
for (let i = 0; i < element.style.length; i++) {
|
1772 |
+
const propName = element.style[i];
|
1773 |
+
const propValue = element.style[propName];
|
1774 |
+
|
1775 |
+
const propDiv = document.createElement('div');
|
1776 |
+
propDiv.className = 'css-property';
|
1777 |
+
|
1778 |
+
const nameSpan = document.createElement('span');
|
1779 |
+
nameSpan.className = 'css-property-name editable';
|
1780 |
+
nameSpan.textContent = propName;
|
1781 |
+
nameSpan.onclick = () => editCSSProperty(element, propName, 'style');
|
1782 |
+
|
1783 |
+
const valueSpan = document.createElement('span');
|
1784 |
+
valueSpan.className = 'css-property-value editable';
|
1785 |
+
valueSpan.textContent = propValue;
|
1786 |
+
valueSpan.onclick = () => editCSSProperty(element, propName, 'style');
|
1787 |
+
|
1788 |
+
const toggleSpan = document.createElement('span');
|
1789 |
+
toggleSpan.className = 'css-toggle';
|
1790 |
+
toggleSpan.textContent = '×';
|
1791 |
+
toggleSpan.title = 'プロパティを無効化';
|
1792 |
+
toggleSpan.onclick = () => {
|
1793 |
+
element.style[propName] = '';
|
1794 |
+
updateCSSPanel(element);
|
1795 |
+
};
|
1796 |
+
|
1797 |
+
propDiv.appendChild(nameSpan);
|
1798 |
+
propDiv.appendChild(valueSpan);
|
1799 |
+
propDiv.appendChild(toggleSpan);
|
1800 |
+
inlineRule.appendChild(propDiv);
|
1801 |
+
}
|
1802 |
+
|
1803 |
+
cssPanel.appendChild(inlineRule);
|
1804 |
+
}
|
1805 |
+
|
1806 |
+
const computedStyles = window.getComputedStyle(element);
|
1807 |
+
const computedRule = document.createElement('div');
|
1808 |
+
computedRule.className = 'css-rule';
|
1809 |
+
|
1810 |
+
const computedSelector = document.createElement('div');
|
1811 |
+
computedSelector.className = 'css-selector';
|
1812 |
+
computedSelector.textContent = '計算されたスタイル';
|
1813 |
+
computedRule.appendChild(computedSelector);
|
1814 |
+
|
1815 |
+
const importantProps = [
|
1816 |
+
'display', 'position', 'width', 'height', 'margin', 'padding',
|
1817 |
+
'color', 'background', 'border', 'font', 'flex', 'grid'
|
1818 |
+
];
|
1819 |
+
|
1820 |
+
importantProps.forEach(prop => {
|
1821 |
+
const value = computedStyles[prop];
|
1822 |
+
|
1823 |
+
const propDiv = document.createElement('div');
|
1824 |
+
propDiv.className = 'css-property';
|
1825 |
+
|
1826 |
+
const nameSpan = document.createElement('span');
|
1827 |
+
nameSpan.className = 'css-property-name';
|
1828 |
+
nameSpan.textContent = prop;
|
1829 |
+
|
1830 |
+
const valueSpan = document.createElement('span');
|
1831 |
+
valueSpan.className = 'css-property-value';
|
1832 |
+
valueSpan.textContent = value;
|
1833 |
+
|
1834 |
+
propDiv.appendChild(nameSpan);
|
1835 |
+
propDiv.appendChild(valueSpan);
|
1836 |
+
computedRule.appendChild(propDiv);
|
1837 |
+
});
|
1838 |
+
|
1839 |
+
cssPanel.appendChild(computedRule);
|
1840 |
+
}
|
1841 |
+
|
1842 |
+
// DOMツリー構築
|
1843 |
+
function buildDOMTree(node, parentElement, depth = 0, isRoot = false) {
|
1844 |
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
1845 |
+
const element = document.createElement('div');
|
1846 |
+
element.className = 'dom-node';
|
1847 |
+
element.style.marginLeft = `${depth * 15}px`;
|
1848 |
+
element.dataset.elementId = node.id || Math.random().toString(36).substr(2, 9);
|
1849 |
+
|
1850 |
+
// 右クリックイベント
|
1851 |
+
element.oncontextmenu = (e) => {
|
1852 |
+
e.preventDefault();
|
1853 |
+
selectedElement = node;
|
1854 |
+
selectedDOMNode = element;
|
1855 |
+
|
1856 |
+
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
1857 |
+
element.classList.add('selected');
|
1858 |
+
|
1859 |
+
// HTML要素にはコンテキストメニューを表示しない
|
1860 |
+
if (node !== document.documentElement) {
|
1861 |
+
contextMenu.style.display = 'block';
|
1862 |
+
contextMenu.style.left = `${e.pageX}px`;
|
1863 |
+
contextMenu.style.top = `${e.pageY}px`;
|
1864 |
+
}
|
1865 |
+
|
1866 |
+
updateCSSPanel(node);
|
1867 |
+
};
|
1868 |
+
|
1869 |
+
// 左クリックで選択
|
1870 |
+
element.onclick = (e) => {
|
1871 |
+
if (e.target.classList.contains('dom-toggle')) return;
|
1872 |
+
|
1873 |
+
e.stopPropagation();
|
1874 |
+
selectedElement = node;
|
1875 |
+
selectedDOMNode = element;
|
1876 |
+
|
1877 |
+
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
1878 |
+
element.classList.add('selected');
|
1879 |
+
|
1880 |
+
updateCSSPanel(node);
|
1881 |
+
};
|
1882 |
+
|
1883 |
+
// 子要素がある場合はトグルボタンを追加
|
1884 |
+
const hasChildren = node.childNodes.length > 0 &&
|
1885 |
+
!(node.childNodes.length === 1 && node.childNodes[0].nodeType === Node.TEXT_NODE && !node.childNodes[0].textContent.trim());
|
1886 |
+
|
1887 |
+
if (hasChildren) {
|
1888 |
+
const toggle = document.createElement('div');
|
1889 |
+
toggle.className = 'dom-toggle';
|
1890 |
+
toggle.onclick = (e) => {
|
1891 |
+
e.stopPropagation();
|
1892 |
+
const children = element.querySelector('.dom-children');
|
1893 |
+
if (children) {
|
1894 |
+
if (children.style.maxHeight === '0px') {
|
1895 |
+
children.style.maxHeight = children.scrollHeight + 'px';
|
1896 |
+
toggle.classList.remove('collapsed');
|
1897 |
+
} else {
|
1898 |
+
children.style.maxHeight = '0px';
|
1899 |
+
toggle.classList.add('collapsed');
|
1900 |
+
}
|
1901 |
+
}
|
1902 |
+
};
|
1903 |
+
element.appendChild(toggle);
|
1904 |
+
}
|
1905 |
+
|
1906 |
+
// タグ名(HTML要素は編集不可)
|
1907 |
+
const tag = document.createElement('span');
|
1908 |
+
tag.className = 'dom-tag';
|
1909 |
+
tag.textContent = `<${node.tagName.toLowerCase()}`;
|
1910 |
+
|
1911 |
+
if (node !== document.documentElement) {
|
1912 |
+
tag.classList.add('editable');
|
1913 |
+
tag.onclick = (e) => {
|
1914 |
+
e.stopPropagation();
|
1915 |
+
startInlineEdit(tag, node.tagName.toLowerCase(), (newValue) => {
|
1916 |
+
const newElement = document.createElement(newValue);
|
1917 |
+
Array.from(node.attributes).forEach(attr => {
|
1918 |
+
newElement.setAttribute(attr.name, attr.value);
|
1919 |
+
});
|
1920 |
+
newElement.innerHTML = node.innerHTML;
|
1921 |
+
node.parentNode.replaceChild(newElement, node);
|
1922 |
+
selectedElement = newElement;
|
1923 |
+
refreshElementsPanel();
|
1924 |
+
});
|
1925 |
+
};
|
1926 |
+
}
|
1927 |
+
|
1928 |
+
element.appendChild(tag);
|
1929 |
+
|
1930 |
+
// 属性(HTML要素は編集不可)
|
1931 |
+
Array.from(node.attributes).forEach(attr => {
|
1932 |
+
const attrSpan = document.createElement('span');
|
1933 |
+
attrSpan.className = 'dom-attr';
|
1934 |
+
attrSpan.textContent = ` ${attr.name}="${attr.value}"`;
|
1935 |
+
|
1936 |
+
if (node !== document.documentElement) {
|
1937 |
+
attrSpan.classList.add('editable');
|
1938 |
+
attrSpan.onclick = (e) => {
|
1939 |
+
e.stopPropagation();
|
1940 |
+
startInlineEdit(attrSpan, attr.value, (newValue) => {
|
1941 |
+
node.setAttribute(attr.name, newValue);
|
1942 |
+
refreshElementsPanel();
|
1943 |
+
});
|
1944 |
+
};
|
1945 |
+
}
|
1946 |
+
|
1947 |
+
element.appendChild(attrSpan);
|
1948 |
+
});
|
1949 |
|
1950 |
+
element.appendChild(document.createTextNode('>'));
|
1951 |
+
|
1952 |
+
if (hasChildren) {
|
1953 |
+
const childrenContainer = document.createElement('div');
|
1954 |
+
childrenContainer.className = 'dom-children';
|
1955 |
+
childrenContainer.style.maxHeight = isRoot ? 'none' : '0px';
|
1956 |
+
|
1957 |
+
node.childNodes.forEach(child => {
|
1958 |
+
buildDOMTree(child, childrenContainer, depth + 1);
|
1959 |
+
});
|
1960 |
+
|
1961 |
+
if (node.tagName.toLowerCase() !== 'br') {
|
1962 |
+
const closeTag = document.createElement('div');
|
1963 |
+
closeTag.style.marginLeft = `${depth * 15}px`;
|
1964 |
+
closeTag.innerHTML = `<span class="dom-tag"></${node.tagName.toLowerCase()}></span>`;
|
1965 |
+
childrenContainer.appendChild(closeTag);
|
1966 |
+
}
|
1967 |
+
|
1968 |
+
element.appendChild(childrenContainer);
|
1969 |
+
}
|
1970 |
+
|
1971 |
+
parentElement.appendChild(element);
|
1972 |
+
} else if (node.nodeType === Node.TEXT_NODE && node.textContent.trim()) {
|
1973 |
+
const text = document.createElement('div');
|
1974 |
+
text.style.marginLeft = `${depth * 15}px`;
|
1975 |
+
text.className = 'dom-text editable';
|
1976 |
+
text.textContent = `"${node.textContent.trim()}"`;
|
1977 |
+
text.onclick = (e) => {
|
1978 |
+
e.stopPropagation();
|
1979 |
+
startInlineEdit(text, node.textContent.trim(), (newValue) => {
|
1980 |
+
node.textContent = newValue;
|
1981 |
+
refreshElementsPanel();
|
1982 |
+
});
|
1983 |
+
};
|
1984 |
+
parentElement.appendChild(text);
|
1985 |
+
}
|
1986 |
+
}
|
1987 |
+
|
1988 |
+
// CSSプロパティ編集
|
1989 |
+
function editCSSProperty(element, propName, styleType) {
|
1990 |
+
let currentValue = '';
|
1991 |
+
|
1992 |
+
if (styleType === 'style') {
|
1993 |
+
currentValue = element.style[propName];
|
1994 |
+
}
|
1995 |
+
|
1996 |
+
const newValue = prompt(`${propName} の新しい値を入力`, currentValue);
|
1997 |
+
|
1998 |
+
if (newValue !== null) {
|
1999 |
+
if (styleType === 'style') {
|
2000 |
+
element.style[propName] = newValue;
|
2001 |
+
}
|
2002 |
+
|
2003 |
+
updateCSSPanel(element);
|
2004 |
+
refreshElementsPanel();
|
2005 |
+
}
|
2006 |
+
}
|
2007 |
+
|
2008 |
+
// DOM変更を監視
|
2009 |
+
observer.observe(document.documentElement, {
|
2010 |
+
childList: true,
|
2011 |
+
subtree: true,
|
2012 |
+
attributes: true,
|
2013 |
+
characterData: true
|
2014 |
+
});
|
2015 |
+
|
2016 |
+
setTimeout(() => {
|
2017 |
+
refreshElementsPanel();
|
2018 |
+
}, 0);
|
2019 |
+
|
2020 |
+
return panel;
|
2021 |
+
}
|
2022 |
// 開発者ツール表示/非表示
|
2023 |
function toggleDevTools() {
|
2024 |
const container = document.getElementById('devtools-container');
|