各位以后要注意先看编辑选项里有没有想要的功能 😂
没仔细看直接想当然的让 AI 搓了 10 分钟搓了个弱化版,当反向示例了这下
类似代码编辑器中移动代码行
可能有神奇的特性,个人暂时只打算用于列表节点的临时交换。
(function() {
const listSwapper = {
// 获取当前文档编辑区
getActiveEditor: function() {
return document.querySelector('.protyle:not(.fn__none) .protyle-wysiwyg');
},
// 判断节点是否为列表项
isListItem: function(node) {
return node && node.classList && node.classList.contains('li');
},
// 判断节点是否为列表容器
isList: function(node) {
return node && node.classList && node.classList.contains('list');
},
// 从当前选择的节点查找最近的列表项节点
findListItemNode: function(node) {
// 如果是文本节点,先获取其父元素
if (node.nodeType === 3) {
node = node.parentElement;
}
// 向上查找,直到找到列表项节点
while (node) {
if (this.isListItem(node)) {
return node;
}
node = node.parentElement;
// 如果超出编辑区范围则停止
if (!node || node.classList.contains('protyle-wysiwyg')) {
return null;
}
}
return null;
},
// 获取同级的前一个列表项
getPreviousListItem: function(listItem) {
if (!this.isListItem(listItem)) return null;
// 直接获取前一个兄弟元素
let prev = listItem.previousElementSibling;
// 确保是列表项
if (prev && this.isListItem(prev)) {
return prev;
}
return null;
},
// 获取同级的后一个列表项
getNextListItem: function(listItem) {
if (!this.isListItem(listItem)) return null;
// 直接获取后一个兄弟元素
let next = listItem.nextElementSibling;
// 确保是列表项
if (next && this.isListItem(next)) {
return next;
}
return null;
},
// 交换两个列表项节点的位置
swapListItems: function(item1, item2) {
if (!item1 || !item2 || !this.isListItem(item1) || !this.isListItem(item2)) {
return false;
}
try {
const parent = item1.parentNode;
// 确保两个节点在同一个父列表中
if (parent !== item2.parentNode) {
console.log('列表项不在同一个父容器中,无法交换');
return false;
}
// 保存当前项的ID用于后续恢复焦点
const itemId = item1.getAttribute('data-node-id');
// 判断相对位置并交换
const next = item1.nextElementSibling;
if (item2 === next) {
// item2紧跟在item1后面,直接调整顺序
parent.insertBefore(item2, item1);
} else {
// 创建节点引用的副本,用于判断顺序
const item1Clone = item1;
const item2Clone = item2;
// 标记item2的下一个节点
const item2Next = item2.nextElementSibling;
// 如果item1在item2之前
if (Array.from(parent.children).indexOf(item1Clone) <
Array.from(parent.children).indexOf(item2Clone)) {
// 将item2移到item1之前
parent.insertBefore(item2, item1);
// 将item1移到item2原来的位置
if (item2Next) {
parent.insertBefore(item1, item2Next);
} else {
parent.appendChild(item1);
}
} else {
// item2在item1之前
// 先将item1移到item2的位置
parent.insertBefore(item1, item2);
// 再将item2移到item1原来的位置
if (next) {
parent.insertBefore(item2, next);
} else {
parent.appendChild(item2);
}
}
}
console.log('列表项交换成功');
return itemId;
} catch (e) {
console.error('交换列表项失败:', e);
return false;
}
},
// 向上移动列表项
moveListItemUp: function() {
// 获取当前选中内容所在的列表项
const selection = window.getSelection();
if (!selection.rangeCount) return;
const listItem = this.findListItemNode(selection.getRangeAt(0).startContainer);
if (!listItem) {
console.log('当前光标不在列表项内');
return;
}
console.log('当前列表项:', listItem);
// 获取同级的前一个列表项
const prevItem = this.getPreviousListItem(listItem);
if (!prevItem) {
console.log('已经是第一个列表项,无法上移');
return;
}
console.log('前一个列表项:', prevItem);
// 交换列表项
const itemId = this.swapListItems(listItem, prevItem);
// 恢复焦点
if (itemId) {
setTimeout(() => {
this.focusListItemById(itemId);
}, 10);
}
},
// 向下移动列表项
moveListItemDown: function() {
// 获取当前选中内容所在的列表项
const selection = window.getSelection();
if (!selection.rangeCount) return;
const listItem = this.findListItemNode(selection.getRangeAt(0).startContainer);
if (!listItem) {
console.log('当前光标不在列表项内');
return;
}
console.log('当前列表项:', listItem);
// 获取同级的后一个列表项
const nextItem = this.getNextListItem(listItem);
if (!nextItem) {
console.log('已经是最后一个列表项,无法下移');
return;
}
console.log('后一个列表项:', nextItem);
// 交换列表项
const itemId = this.swapListItems(listItem, nextItem);
// 恢复焦点
if (itemId) {
setTimeout(() => {
this.focusListItemById(itemId);
}, 10);
}
},
// 通过ID聚焦到列表项
focusListItemById: function(itemId) {
if (!itemId) return;
// 查找具有特定ID的列表项
const item = document.querySelector(`[data-node-id="${itemId}"]`);
if (!item) return;
// 查找列表项中的可编辑段落
const paragraph = item.querySelector('[contenteditable="true"]');
if (!paragraph) return;
// 聚焦并将光标设置到段落末尾
paragraph.focus();
const range = document.createRange();
const selection = window.getSelection();
// 将光标设置到段落内容末尾
if (paragraph.childNodes.length > 0) {
const lastChild = paragraph.childNodes[paragraph.childNodes.length - 1];
if (lastChild.nodeType === Node.TEXT_NODE) {
range.setStart(lastChild, lastChild.length);
range.setEnd(lastChild, lastChild.length);
} else {
range.selectNodeContents(lastChild);
range.collapse(false);
}
} else {
range.selectNodeContents(paragraph);
range.collapse(false);
}
selection.removeAllRanges();
selection.addRange(range);
},
// 初始化键盘事件监听
init: function() {
document.addEventListener('keydown', (event) => {
// 检查是否是Alt+箭头组合键
if (event.altKey && !event.ctrlKey && !event.shiftKey && !event.metaKey) {
// Alt + 上箭头:向上移动列表项
if (event.key === 'ArrowUp') {
event.preventDefault();
this.moveListItemUp();
return false;
}
// Alt + 下箭头:向下移动列表项
else if (event.key === 'ArrowDown') {
event.preventDefault();
this.moveListItemDown();
return false;
}
}
});
console.log('思源笔记列表项交换脚本已加载,使用 Alt+↑/↓ 来交换列表项位置');
}
};
// 初始化脚本
listSwapper.init();
})();
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于