Update dev-tools.js
Browse files- dev-tools.js +96 -77
dev-tools.js
CHANGED
@@ -618,11 +618,20 @@
|
|
618 |
if (!selectedElement) return;
|
619 |
|
620 |
switch (action) {
|
621 |
-
|
622 |
-
|
623 |
-
|
624 |
-
|
625 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
626 |
break;
|
627 |
case 'add-attribute':
|
628 |
const attrName = prompt('属性名を入力');
|
@@ -676,7 +685,7 @@
|
|
676 |
}
|
677 |
|
678 |
// インライン編集関数
|
679 |
-
|
680 |
if (activeEditElement) return;
|
681 |
|
682 |
const originalValue = element.textContent;
|
@@ -695,46 +704,51 @@
|
|
695 |
input.select();
|
696 |
|
697 |
activeEditElement = {
|
698 |
-
|
699 |
-
|
700 |
-
|
701 |
};
|
702 |
|
703 |
const clickOutsideHandler = (e) => {
|
704 |
-
|
705 |
-
|
706 |
-
|
707 |
};
|
708 |
|
709 |
input.addEventListener('keydown', (e) => {
|
710 |
-
|
711 |
-
|
712 |
-
|
713 |
-
|
714 |
-
|
715 |
});
|
716 |
|
717 |
setTimeout(() => {
|
718 |
-
|
719 |
}, 0);
|
720 |
|
721 |
function finishInlineEdit() {
|
722 |
-
|
723 |
-
|
724 |
-
|
725 |
-
|
|
|
|
|
|
|
|
|
|
|
726 |
}
|
727 |
|
728 |
function cancelInlineEdit() {
|
729 |
-
|
730 |
}
|
731 |
|
732 |
function cleanup() {
|
733 |
-
|
734 |
-
|
735 |
-
|
736 |
}
|
737 |
-
|
738 |
|
739 |
// Consoleパネル作成
|
740 |
function createConsolePanel() {
|
@@ -918,69 +932,74 @@
|
|
918 |
}
|
919 |
|
920 |
// DOMツリー構築
|
921 |
-
|
922 |
-
|
923 |
const element = document.createElement('div');
|
924 |
element.className = 'dom-node';
|
925 |
element.style.marginLeft = `${depth * 15}px`;
|
926 |
element.dataset.elementId = node.id || Math.random().toString(36).substr(2, 9);
|
927 |
|
|
|
928 |
element.oncontextmenu = (e) => {
|
929 |
-
|
930 |
-
|
931 |
-
|
932 |
-
|
933 |
-
|
934 |
-
|
935 |
-
|
936 |
-
|
937 |
-
|
938 |
-
|
939 |
-
|
940 |
-
|
941 |
-
|
942 |
-
|
943 |
-
|
944 |
-
e.stopPropagation();
|
945 |
-
selectedElement = node;
|
946 |
-
selectedDOMNode = element;
|
947 |
-
|
948 |
-
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
949 |
-
element.classList.add('selected');
|
950 |
-
|
951 |
-
updateCSSPanel(node);
|
952 |
};
|
953 |
|
|
|
954 |
const tag = document.createElement('span');
|
955 |
-
tag.className = 'dom-tag
|
956 |
tag.textContent = `<${node.tagName.toLowerCase()}`;
|
957 |
-
|
958 |
-
|
959 |
-
|
960 |
-
|
961 |
-
|
962 |
-
|
963 |
-
|
964 |
-
|
965 |
-
|
966 |
-
|
967 |
-
|
968 |
-
|
969 |
-
|
|
|
|
|
|
|
|
|
|
|
970 |
element.appendChild(tag);
|
971 |
|
|
|
972 |
Array.from(node.attributes).forEach(attr => {
|
973 |
-
|
974 |
-
|
975 |
-
|
976 |
-
|
977 |
-
|
978 |
-
|
979 |
-
|
980 |
-
|
981 |
-
|
982 |
-
|
983 |
-
|
|
|
|
|
|
|
|
|
|
|
984 |
});
|
985 |
|
986 |
element.appendChild(document.createTextNode('>'));
|
|
|
618 |
if (!selectedElement) return;
|
619 |
|
620 |
switch (action) {
|
621 |
+
case 'edit-html':
|
622 |
+
// document.documentElement(html要素)は編集不可
|
623 |
+
if (selectedElement === document.documentElement) {
|
624 |
+
alert('ルートHTML要素は直接編集できません');
|
625 |
+
return;
|
626 |
+
}
|
627 |
+
startInlineEdit(selectedDOMNode, selectedElement.outerHTML, (newValue) => {
|
628 |
+
try {
|
629 |
+
selectedElement.outerHTML = newValue;
|
630 |
+
refreshElementsPanel();
|
631 |
+
} catch (e) {
|
632 |
+
alert('この要素は編集できません: ' + e.message);
|
633 |
+
}
|
634 |
+
});
|
635 |
break;
|
636 |
case 'add-attribute':
|
637 |
const attrName = prompt('属性名を入力');
|
|
|
685 |
}
|
686 |
|
687 |
// インライン編集関数
|
688 |
+
function startInlineEdit(element, initialValue, callback) {
|
689 |
if (activeEditElement) return;
|
690 |
|
691 |
const originalValue = element.textContent;
|
|
|
704 |
input.select();
|
705 |
|
706 |
activeEditElement = {
|
707 |
+
element: element,
|
708 |
+
input: input,
|
709 |
+
callback: callback
|
710 |
};
|
711 |
|
712 |
const clickOutsideHandler = (e) => {
|
713 |
+
if (!input.contains(e.target)) {
|
714 |
+
finishInlineEdit();
|
715 |
+
}
|
716 |
};
|
717 |
|
718 |
input.addEventListener('keydown', (e) => {
|
719 |
+
if (e.key === 'Enter') {
|
720 |
+
finishInlineEdit();
|
721 |
+
} else if (e.key === 'Escape') {
|
722 |
+
cancelInlineEdit();
|
723 |
+
}
|
724 |
});
|
725 |
|
726 |
setTimeout(() => {
|
727 |
+
document.addEventListener('click', clickOutsideHandler);
|
728 |
}, 0);
|
729 |
|
730 |
function finishInlineEdit() {
|
731 |
+
try {
|
732 |
+
if (input.value !== originalValue && callback) {
|
733 |
+
callback(input.value);
|
734 |
+
}
|
735 |
+
} catch (e) {
|
736 |
+
alert('編集に失敗しました: ' + e.message);
|
737 |
+
} finally {
|
738 |
+
cleanup();
|
739 |
+
}
|
740 |
}
|
741 |
|
742 |
function cancelInlineEdit() {
|
743 |
+
cleanup();
|
744 |
}
|
745 |
|
746 |
function cleanup() {
|
747 |
+
document.removeEventListener('click', clickOutsideHandler);
|
748 |
+
input.remove();
|
749 |
+
activeEditElement = null;
|
750 |
}
|
751 |
+
}
|
752 |
|
753 |
// Consoleパネル作成
|
754 |
function createConsolePanel() {
|
|
|
932 |
}
|
933 |
|
934 |
// DOMツリー構築
|
935 |
+
function buildDOMTree(node, parentElement, depth = 0) {
|
936 |
+
if (node.nodeType === Node.ELEMENT_NODE) {
|
937 |
const element = document.createElement('div');
|
938 |
element.className = 'dom-node';
|
939 |
element.style.marginLeft = `${depth * 15}px`;
|
940 |
element.dataset.elementId = node.id || Math.random().toString(36).substr(2, 9);
|
941 |
|
942 |
+
// 右クリックイベント
|
943 |
element.oncontextmenu = (e) => {
|
944 |
+
e.preventDefault();
|
945 |
+
selectedElement = node;
|
946 |
+
selectedDOMNode = element;
|
947 |
+
|
948 |
+
document.querySelectorAll('.dom-node').forEach(el => el.classList.remove('selected'));
|
949 |
+
element.classList.add('selected');
|
950 |
+
|
951 |
+
// HTML要素にはコンテキストメニューを表示しない
|
952 |
+
if (node !== document.documentElement) {
|
953 |
+
contextMenu.style.display = 'block';
|
954 |
+
contextMenu.style.left = `${e.pageX}px`;
|
955 |
+
contextMenu.style.top = `${e.pageY}px`;
|
956 |
+
}
|
957 |
+
|
958 |
+
updateCSSPanel(node);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
959 |
};
|
960 |
|
961 |
+
// タグ名(HTML要素は編集不可)
|
962 |
const tag = document.createElement('span');
|
963 |
+
tag.className = 'dom-tag';
|
964 |
tag.textContent = `<${node.tagName.toLowerCase()}`;
|
965 |
+
|
966 |
+
if (node !== document.documentElement) {
|
967 |
+
tag.classList.add('editable');
|
968 |
+
tag.onclick = (e) => {
|
969 |
+
e.stopPropagation();
|
970 |
+
startInlineEdit(tag, node.tagName.toLowerCase(), (newValue) => {
|
971 |
+
const newElement = document.createElement(newValue);
|
972 |
+
Array.from(node.attributes).forEach(attr => {
|
973 |
+
newElement.setAttribute(attr.name, attr.value);
|
974 |
+
});
|
975 |
+
newElement.innerHTML = node.innerHTML;
|
976 |
+
node.parentNode.replaceChild(newElement, node);
|
977 |
+
selectedElement = newElement;
|
978 |
+
refreshElementsPanel();
|
979 |
+
});
|
980 |
+
};
|
981 |
+
}
|
982 |
+
|
983 |
element.appendChild(tag);
|
984 |
|
985 |
+
// 属性(HTML要素は編集不可)
|
986 |
Array.from(node.attributes).forEach(attr => {
|
987 |
+
const attrSpan = document.createElement('span');
|
988 |
+
attrSpan.className = 'dom-attr';
|
989 |
+
attrSpan.textContent = ` ${attr.name}="${attr.value}"`;
|
990 |
+
|
991 |
+
if (node !== document.documentElement) {
|
992 |
+
attrSpan.classList.add('editable');
|
993 |
+
attrSpan.onclick = (e) => {
|
994 |
+
e.stopPropagation();
|
995 |
+
startInlineEdit(attrSpan, attr.value, (newValue) => {
|
996 |
+
node.setAttribute(attr.name, newValue);
|
997 |
+
refreshElementsPanel();
|
998 |
+
});
|
999 |
+
};
|
1000 |
+
}
|
1001 |
+
|
1002 |
+
element.appendChild(attrSpan);
|
1003 |
});
|
1004 |
|
1005 |
element.appendChild(document.createTextNode('>'));
|