求一个功能,
一个目标列表中有多个子目标, 当子目标全部勾选后, 父目标自动勾选,
当子目标取消勾选后, 父目标也自动取消勾选
支持多层级目标列表, 即当三级目标列表完成后, 二级目标列表自动勾选, 当此二级目标列表为最后一个未勾选的, 这一级目标列表自动勾选
求一个功能,
一个目标列表中有多个子目标, 当子目标全部勾选后, 父目标自动勾选,
当子目标取消勾选后, 父目标也自动取消勾选
支持多层级目标列表, 即当三级目标列表完成后, 二级目标列表自动勾选, 当此二级目标列表为最后一个未勾选的, 这一级目标列表自动勾选
思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。
融合块、大纲和双向链接,重构你的思维。
代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。
用户在该标签下分享代码片段时需在帖子标题前添加 [css] 或 [js] 用于区分代码片段类型。
提问之前请先看《提问的智慧》,好的问题比好的答案更有价值。
试试这个

/*
// 自动勾选取消父任务
see https://ld246.com/article/1755052956165
version 0.0.2
0.0.2 增加新需求
需求:
一个任务列表中有多个子任务, 当子任务全部勾选后, 父任务自动勾选,
当子任务有一个取消勾选后, 父任务也自动取消勾选
支持多层级任务列表, 比如,当三级任务列表都勾选后, 二级任务列表自动勾选,
当此二级任务列表为最后一个未勾选的, 那么这第一级任务列表也自动勾选
新增需求:
1. 当一个已完成的父任务新增一个子任务时,由于新子任务默认是未完成(未勾选),所以父任务应自动取消勾选。
2. 当父任务下只剩一个未完成子任务时,若删除该子任务,则父任务应自动变为已完成(勾选)。
*/
setTimeout(() => {
const container = document.querySelector('.layout__center, #editor');
if (!container) return;
container.addEventListener('click', async (e) => {
if (e.altKey || e.shiftKey || e.ctrlKey || e.metaKey || !e.target.closest('.protyle-action--task')) return;
await new Promise(resolve => setTimeout(resolve, 50));
// 获取祖先元素并模拟点击
const item = e.target.closest('[data-subtype="t"][data-type="NodeListItem"]');
clickParentItems(item);
}, true);
// 观察 container 的子树变化(基于规律的处理,可能有bug)
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
// 处理新增的子任务
for (const node of mutation.addedNodes) {
if (node.nodeType !== 1) continue;
if (node.matches('div[data-type="NodeListItem"][data-subtype="t"]')) {
clickParentItems(node);
}
else if (node.matches('div[data-type="NodeList"][data-subtype="t"]')) {
const items = node.querySelectorAll('div[data-type="NodeListItem"][data-subtype="t"]');
items.forEach(item => clickParentItems(item));
} else {
const items = node.querySelectorAll('div[data-type="NodeListItem"][data-subtype="t"]');
if (items.length) items.forEach(item => clickParentItems(item));
}
}
// 处理删除的子任务
for (const node of mutation.removedNodes) {
if (node.nodeType !== 1) continue;
if (node.matches('div[data-type="NodeListItem"][data-subtype="t"]')) {
clickParentItems(mutation.target.querySelector('div[data-type="NodeListItem"][data-subtype="t"]'));
}
}
}
});
observer.observe(container, { childList: true, subtree: true, attributes: false });
function clickParentItems(item) {
const parentItems = getParentsItems(item);
if (parentItems.length === 0) return;
for (const item of parentItems) {
const list = item?.closest('[data-type="NodeList"][data-subtype="t"]');
const hasUnDone = list?.querySelector(':scope > [data-subtype="t"][data-type="NodeListItem"] > .protyle-action--task use[*|href="#iconUncheck"]');
const parentItem = item.parentElement?.closest('[data-type="NodeListItem"][data-subtype="t"]');
const parentTaskCheck = parentItem?.querySelector('.protyle-action--task');
const parentIsChecked = parentTaskCheck?.querySelector('use[*|href="#iconUncheck"]') ? false : true;
// 当存在不可勾选的任务项目时,终止,不再向上级遍历,如果你想继续遍历可把此处的return改为continue
if (!parentTaskCheck) return;
if (hasUnDone) {
// 当有未完成的子任务时,如果父任务已勾选需求要取消
if (parentIsChecked) parentTaskCheck.click();
} else {
// 当全部勾选了子任务时,如果父任务未勾选则勾选
if (!parentIsChecked) parentTaskCheck.click();
}
}
}
// 获取祖先任务项
function getParentsItems(item, includeSelf = true) {
if (!item) return [];
const items = [];
// 是否包含自己
if (includeSelf) {
items.push(item);
}
// 向上遍历所有符合条件的祖先 NodeListItem
let current = item;
while (true) {
const parentItem = current.parentElement?.closest('[data-subtype="t"][data-type="NodeListItem"]');
if (!parentItem) break;
items.push(parentItem);
current = parentItem;
}
return items;
}
}, 2000);
题外话:
今天发生了一件奇怪的事,我写代码时看了下时间 6:08,写完,又看了下时间还是 6:08,秒没注意。
难道刚刚时间静止了?之前也出现过这种错觉。
好吧,如果以后写代码时都能时间静止就好了,就可以无敌了
后人有诗叹曰
:
屏光冷映键飞忙,
回看时针竟未航。
若得封时锁宙宇,
Deadline 前自徜徉。
试试这个

/*
// 自动勾选取消父任务
see https://ld246.com/article/1755052956165
version 0.0.2
0.0.2 增加新需求
需求:
一个任务列表中有多个子任务, 当子任务全部勾选后, 父任务自动勾选,
当子任务有一个取消勾选后, 父任务也自动取消勾选
支持多层级任务列表, 比如,当三级任务列表都勾选后, 二级任务列表自动勾选,
当此二级任务列表为最后一个未勾选的, 那么这第一级任务列表也自动勾选
新增需求:
1. 当一个已完成的父任务新增一个子任务时,由于新子任务默认是未完成(未勾选),所以父任务应自动取消勾选。
2. 当父任务下只剩一个未完成子任务时,若删除该子任务,则父任务应自动变为已完成(勾选)。
*/
setTimeout(() => {
const container = document.querySelector('.layout__center, #editor');
if (!container) return;
container.addEventListener('click', async (e) => {
if (e.altKey || e.shiftKey || e.ctrlKey || e.metaKey || !e.target.closest('.protyle-action--task')) return;
await new Promise(resolve => setTimeout(resolve, 50));
// 获取祖先元素并模拟点击
const item = e.target.closest('[data-subtype="t"][data-type="NodeListItem"]');
clickParentItems(item);
}, true);
// 观察 container 的子树变化(基于规律的处理,可能有bug)
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
// 处理新增的子任务
for (const node of mutation.addedNodes) {
if (node.nodeType !== 1) continue;
if (node.matches('div[data-type="NodeListItem"][data-subtype="t"]')) {
clickParentItems(node);
}
else if (node.matches('div[data-type="NodeList"][data-subtype="t"]')) {
const items = node.querySelectorAll('div[data-type="NodeListItem"][data-subtype="t"]');
items.forEach(item => clickParentItems(item));
} else {
const items = node.querySelectorAll('div[data-type="NodeListItem"][data-subtype="t"]');
if (items.length) items.forEach(item => clickParentItems(item));
}
}
// 处理删除的子任务
for (const node of mutation.removedNodes) {
if (node.nodeType !== 1) continue;
if (node.matches('div[data-type="NodeListItem"][data-subtype="t"]')) {
clickParentItems(mutation.target.querySelector('div[data-type="NodeListItem"][data-subtype="t"]'));
}
}
}
});
observer.observe(container, { childList: true, subtree: true, attributes: false });
function clickParentItems(item) {
const parentItems = getParentsItems(item);
if (parentItems.length === 0) return;
for (const item of parentItems) {
const list = item?.closest('[data-type="NodeList"][data-subtype="t"]');
const hasUnDone = list?.querySelector(':scope > [data-subtype="t"][data-type="NodeListItem"] > .protyle-action--task use[*|href="#iconUncheck"]');
const parentItem = item.parentElement?.closest('[data-type="NodeListItem"][data-subtype="t"]');
const parentTaskCheck = parentItem?.querySelector('.protyle-action--task');
const parentIsChecked = parentTaskCheck?.querySelector('use[*|href="#iconUncheck"]') ? false : true;
// 当存在不可勾选的任务项目时,终止,不再向上级遍历,如果你想继续遍历可把此处的return改为continue
if (!parentTaskCheck) return;
if (hasUnDone) {
// 当有未完成的子任务时,如果父任务已勾选需求要取消
if (parentIsChecked) parentTaskCheck.click();
} else {
// 当全部勾选了子任务时,如果父任务未勾选则勾选
if (!parentIsChecked) parentTaskCheck.click();
}
}
}
// 获取祖先任务项
function getParentsItems(item, includeSelf = true) {
if (!item) return [];
const items = [];
// 是否包含自己
if (includeSelf) {
items.push(item);
}
// 向上遍历所有符合条件的祖先 NodeListItem
let current = item;
while (true) {
const parentItem = current.parentElement?.closest('[data-subtype="t"][data-type="NodeListItem"]');
if (!parentItem) break;
items.push(parentItem);
current = parentItem;
}
return items;
}
}, 2000);
题外话:
今天发生了一件奇怪的事,我写代码时看了下时间 6:08,写完,又看了下时间还是 6:08,秒没注意。
难道刚刚时间静止了?之前也出现过这种错觉。
好吧,如果以后写代码时都能时间静止就好了,就可以无敌了
后人有诗叹曰
:
屏光冷映键飞忙,
回看时针竟未航。
若得封时锁宙宇,
Deadline 前自徜徉。
Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的 Linux 操作系统,其名称来自非洲南部祖鲁语或豪萨语的“ubuntu”一词,意思是“人性”、“我的存在是因为大家的存在”,是非洲传统的一种价值观,类似华人社会的“仁爱”思想。Ubuntu 的目标在于为一般用户提供一个最新的、同时又相当稳定的主要由自由软件构建而成的操作系统。
Git 是 Linux Torvalds 为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
这是一个不能说的秘密。
MySQL 是一个关系型数据库管理系统,由瑞典 MySQL AB 公司开发,目前属于 Oracle 公司。MySQL 是最流行的关系型数据库管理系统之一。
微软是一家美国跨国科技公司,也是世界 PC 软件开发的先导,由比尔·盖茨与保罗·艾伦创办于 1975 年,公司总部设立在华盛顿州的雷德蒙德(Redmond,邻近西雅图)。以研发、制造、授权和提供广泛的电脑软件服务业务为主。
JRebel 是一款 Java 虚拟机插件,它使得 Java 程序员能在不进行重部署的情况下,即时看到代码的改变对一个应用程序带来的影响。
WiFiDog 是一套开源的无线热点认证管理工具,主要功能包括:位置相关的内容递送;用户认证和授权;集中式网络监控。
汐洛彖夲肜矩阵(Sillot T☳Converbenk Matrix),致力于服务智慧新彖乄,具有彖乄驱动、极致优雅、开发者友好的特点。其中汐洛绞架(Sillot-Gibbet)基于自思源笔记(siyuan-note),前身是思源笔记汐洛版(更早是思源笔记汐洛分支),是智慧新录乄终端(多端融合,移动端优先)。
主仓库地址:Hi-Windom/Sillot
文档地址:sillot.db.sc.cn
注意事项:
Facebook 是一个联系朋友的社交工具。大家可以通过它和朋友、同事、同学以及周围的人保持互动交流,分享无限上传的图片,发布链接和视频,更可以增进对朋友的了解。
开源中国是目前中国最大的开源技术社区。传播开源的理念,推广开源项目,为 IT 开发者提供了一个发现、使用、并交流开源技术的平台。目前开源中国社区已收录超过两万款开源软件。
百度应用引擎(Baidu App Engine)提供了 PHP、Java、Python 的执行环境,以及云存储、消息服务、云数据库等全面的云服务。它可以让开发者实现自动地部署和管理应用,并且提供动态扩容和负载均衡的运行环境,让开发者不用考虑高成本的运维工作,只需专注于业务逻辑,大大降低了开发者学习和迁移的成本。
千千块(自定义块 css 和 js)
可以用 ai 提示词来无限创作思源笔记
Bootstrap 是 Twitter 推出的一个用于前端开发的开源工具包。它由 Twitter 的设计师 Mark Otto 和 Jacob Thornton 合作开发,是一个 CSS / HTML 框架。
记录并分享人生的经历。
PHP(Hypertext Preprocessor)是一种开源脚本语言。语法吸收了 C 语言、 Java 和 Perl 的特点,主要适用于 Web 开发领域,据说是世界上最好的编程语言。
Typecho 是一款博客程序,它在 GPLv2 许可证下发行,基于 PHP 构建,可以运行在各种平台上,支持多种数据库(MySQL、PostgreSQL、SQLite)。
互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。
又拍云是国内领先的 CDN 服务提供商,国家工信部认证通过的“可信云”,乌云众测平台认证的“安全云”,为移动时代的创业者提供新一代的 CDN 加速服务。
百度(Nasdaq:BIDU)是全球最大的中文搜索引擎、最大的中文网站。2000 年 1 月由李彦宏创立于北京中关村,致力于向人们提供“简单,可依赖”的信息获取方式。“百度”二字源于中国宋朝词人辛弃疾的《青玉案·元夕》词句“众里寻他千百度”,象征着百度对中文信息检索技术的执著追求。
昆明尊园房地产经纪有限公司,即:Kunming Zunyuan Property Agency Company Limited(简称“尊园地产”)于 2007 年 6 月开始筹备,2007 年 8 月 18 日正式成立,注册资本 200 万元,公司性质为股份经纪有限公司,主营业务为:代租、代售、代办产权过户、办理银行按揭、担保、抵押、评估等。
Tomcat 最早是由 Sun Microsystems 开发的一个 Servlet 容器,在 1999 年被捐献给 ASF(Apache Software Foundation),隶属于 Jakarta 项目,现在已经独立为一个顶级项目。Tomcat 主要实现了 JavaEE 中的 Servlet、JSP 规范,同时也提供 HTTP 服务,是市场上非常流行的 Java Web 容器。
Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。
SQLite 是一个进程内的库,实现了自给自足的、无服务器的、零配置的、事务性的 SQL 数据库引擎。SQLite 是全世界使用最为广泛的数据库引擎。
小薇是一个用 Java 写的 QQ 聊天机器人 Web 服务,可以用于社群互动。
由于 Smart QQ 从 2019 年 1 月 1 日起停止服务,所以该项目也已经停止维护了!
脑图又叫思维导图,是表达发散性思维的有效图形思维工具 ,它简单却又很有效,是一种实用性的思维工具。
Gitea 是一个开源社区驱动的轻量级代码托管解决方案,后端采用 Go 编写,采用 MIT 许可证。