行内 js,顾名思义,就是在块内插入可执行的 js 代码。
比较适用于在行内执行一些简单的 js,比如获取天气,获取当前日期和时间,生成倒计时,数据统计等。
效果
使用方法
注意:需要在设置-> 编辑器配置中,开启 Markdown 行级公式语法 感谢 @Fighter93 提醒!
内置函数使用手册
函数内内置了一些变量和方法,比如:
element
或 el
行内 js 元素对象
block
行内 js 所在元素的块信息
doc
行内 js 所在文档的信息
protyle
行内 js 所在文档的 protyle 对象信息
querySql
SQL 查询函数
fetchSyncPost
api 获取函数
date
日期和时间对象
sleep
休眠函数
render
渲染行内 js 元素内容函数
更多变量或函数详细使用说明如下:
(注意,以下文档由 ai 生成,仅供参考)
以下变量和函数主要用于在 思源笔记(Siyuan) 中实现 行内 JavaScript (inline JS) 功能,包括动态执行 JS 代码、与文档块交互、获取环境信息、插入内容、监听 DOM 等功能。
🧩 变量说明
el
, element
- 类型:
HTMLElement
- 用途: 当前正在处理的行内 JS 元素(
<span custom-js="...">
)。 - 示例:
el.innerHTML = 'Hello, world!';
block
- 类型:
Object
- 用途: 当前行内 JS 所属的 文档块(Block) 的相关信息。
- 结构:
{ id: "block-id", // 块 ID element: HTMLElement, // 对应的 DOM 元素 ... }
- 示例:
console.log(block.id); // 输出当前块的 ID
doc
- 类型:
Object
- 用途: 当前文档的信息(即该 block 所属的文档)。
- 结构:
{ id: "doc-id", root_id: "root-block-id", ... }
- 示例:
console.log(doc.root_id); // 获取当前文档的根块 ID
protyle
- 类型:
Object
- 用途: 当前编辑器实例对象,用于操作文档内容。
- 常用方法:
protyle.block.rootID
: 当前文档的 IDprotyle.modal.editor
: 编辑器核心对象
- 示例:
console.log(protyle.block.rootID);
📦 函数说明
querySql(sql)
- 参数: SQL 查询语句
- 返回值: Promise,包含查询结果
- 用途: 查询数据库中的数据(如块、文档等)
- 示例:
const result = await querySql("SELECT * FROM blocks WHERE id='xxx'"); console.log(result);
fetchSyncPost(url, data, returnType)
- 参数:
url
: 请求地址data
: 请求体(可为 JSON 或 FormData)returnType
: 返回格式 (json
或text
)
- 返回值: Promise,包含请求结果
- 用途: 向后端发起同步 POST 请求
- 示例:
const res = await fetchSyncPost("/api/block/updateBlock", { id: "xxx", data: "<p>new content</p>", });
updateBlock(block, data, type)
- 参数:
block
: 要更新的块对象或其 DOM 元素data
: 更新的内容type
: 数据类型(默认"dom"
)
- 用途: 更新指定块的内容
- 示例:
updateBlock(block, "<p>New content</p>");
date
- 类型:
Object
- 用途:包含当前日期时间的对象
- 结构:
{ now: "2025-04-05 12:34:56", today: "2025-04-05", datetime: "2025-04-05 12:34:56", date: "2025-04-05", time: "12:34:56" }
- 示例:
console.log(date.now); // 输出当前完整时间
sleep(ms)
- 参数: 毫秒数
- 用途: 等待指定毫秒数(异步)
- 示例:
await sleep(1000); // 等待 1 秒
whenElementExistOrNull(selector, node, timeout)
- 参数:
selector
: CSS 选择器node
: 查找范围(可选,默认 document)timeout
: 超时时间(毫秒)
- 返回值: Promise,找到元素则 resolve,否则 null
- 用途: 等待某个元素出现
- 示例:
const el = await whenElementExistOrNull("#my-element"); if (el) el.style.color = "red";
whenElementExistOrNullByObserver(selector, node, timeout)
- 参数: 同上
- 用途: 使用
MutationObserver
监听 DOM 变化,等待元素出现 - 区别: 更加稳定地监听 DOM 插入事件
- 示例:
const el = await whenElementExistOrNullByObserver(".loading"); if (el) el.remove();
isMobile()
- 返回值:
boolean
- 用途: 判断是否是移动端
- 示例:
if (isMobile()) alert("这是移动设备");
isMac()
- 返回值:
boolean
- 用途: 判断是否是 macOS 系统
- 示例:
if (isMac()) console.log("运行在 Mac 上");
isWindows()
- 返回值:
boolean
- 用途: 判断是否是 Windows 系统
- 示例:
if (isWindows()) console.log("运行在 Windows 上");
addStyle(css)
- 参数: CSS 样式字符串
- 用途: 动态添加样式到
<head>
- 示例:
addStyle("body { background-color: #f0f0f0; }");
insertCursorAfterElement(el)
- 参数: 要插入光标位置的 DOM 元素
- 用途: 将光标插入到该元素之后
- 示例:
insertCursorAfterElement(document.getElementById("my-element"));
insertToEditor(text)
- 参数: 要插入的文本或 HTML 内容
- 用途: 在当前光标位置插入内容到编辑器
- 示例:
insertToEditor("<p>Hello, world!</p>");
getCursorElement()
- 返回值: 当前光标所在的 DOM 元素
- 用途: 获取用户当前输入的位置
- 示例:
const currentEl = getCursorElement(); console.log(currentEl.textContent);
getShareData(key)
- 参数: 键名
- 用途: 获取全局共享数据(通过
shareData
) - 示例:
const value = await getShareData("user"); console.log(value);
setShareData(key, value)
- 参数: 键名和值
- 用途: 设置全局共享数据
- 示例:
setShareData("user", { name: "Alice" });
shareData
- 类型:
Object
- 用途: 全局共享的数据对象
- 示例:
shareData.myValue = "Hello";
render(element, html, append)
- 参数:
element
: 要渲染的目标 DOM 元素html
: 要渲染的 HTML 字符串append
: 是否追加(true
表示追加)
- 用途: 渲染 HTML 到指定元素
- 示例:
render(document.getElementById("container"), "<p>Rendered Content</p>", false);
✅ 总结
名称 | 类型 | 用途 |
---|---|---|
el , element |
HTMLElement |
当前行内 JS 元素 |
block |
Object |
当前块信息 |
doc |
Object |
文档信息 |
protyle |
Object |
文档对象 |
querySql |
Function |
查询数据库 |
fetchSyncPost |
Function |
发起 POST 请求 |
updateBlock |
Function |
更新文档块 |
date |
Object |
当前日期和时间对象 |
sleep |
Function |
异步等待 |
whenElementExistOrNull |
Function |
等待元素出现 |
whenElementExistOrNullByObserver |
Function |
使用 MutationObserver 等待元素 |
isMobile , isMac , isWindows |
Function |
检测设备系统 |
addStyle |
Function |
添加样式 |
insertCursorAfterElement |
Function |
插入光标到某元素后 |
insertToEditor |
Function |
插入内容到编辑器 |
getCursorElement |
Function |
获取当前光标所在元素 |
getShareData / setShareData |
Function |
读写全局共享数据 |
shareData |
Object |
全局共享数据对象 |
render |
Function |
渲染 HTML 到元素 |
使用示例
获取天气
const weather = await fetch('https://wttr.in/?format=1'); const text = await weather.text(); return "今日天气:" + text.trim();
获取当前日期和时间
return date.datetime;
生成倒计时
function countdownTo(targetDate) { const now = new Date(); const target = new Date(targetDate); // 计算时间差(毫秒) const diffMs = target - now; if (diffMs <= 0) { return "目标日期已过!"; } // 转换为天数 const days = Math.floor(diffMs / (1000 * 60 * 60 * 24)); return `距离 ${targetDate} 还有 <strong>${days}</strong> 天`; } return countdownTo("2026-01-01");
颜色渐变动画
let hue = 0; if(shareData.intervalId) clearInterval(shareData.intervalId); shareData.intervalId = setInterval(() => { hue = (hue + 2) % 360; el.style.color = `hsl(${hue}, 100%, 50%)`; }, 50); return 'Hello Inline JS';
打字机效果
const text = "Hello Inline JS"; let index = 0; function typeWriter(element, text) { return new Promise((resolve) => { let index = 0; const interval = () => { if (index < text.length) { element.textContent += text.charAt(index); index++; setTimeout(interval, 200); } else { resolve(); } }; interval(); }); } element.textContent = ''; // 先清空元素内容 await typeWriter(element, text); // 启动打字效果 return element.textContent; // 返回已打完的文本
数据统计
把下面代码粘贴到模板或文档中即可
{{{col {{{row 今日更新文档:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5LuK5pel77yI5pys5Zyw5pe26Ze077yaMDA6MDA6MDAg5YiwIDIzOjU5OjU577yJCiAgICBzZWxlY3QgY291bnQoKikgYXMgY291bnQgZnJvbSBibG9ja3MKICAgIHdoZXJlIHR5cGU9J2QnCiAgICBhbmQgdXBkYXRlZCA+PSBzdHJmdGltZSgnJVklbSVkJUglTSVTJywgZGF0ZSgnbm93JywgJ2xvY2FsdGltZScpLCAnc3RhcnQgb2YgZGF5JykKICAgIGFuZCB1cGRhdGVkIDwgc3RyZnRpbWUoJyVZJW0lZCVIJU0lUycsIGRhdGUoJ25vdycsICdsb2NhbHRpbWUnKSwgJ3N0YXJ0IG9mIGRheScsICcrMSBkYXknKQogICAgbGltaXQgMTsKYCk7CnJldHVybiBjb3VudFswXT8uY291bnQgfHwgMDs="} {: id="20250614064338-13shwt1" updated="20250614073552"} 今日更新块:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5LuK5pel77yI5pys5Zyw5pe26Ze077yaMDA6MDA6MDAg5YiwIDIzOjU5OjU577yJCiAgICBzZWxlY3QgY291bnQoKikgYXMgY291bnQgZnJvbSBibG9ja3MKICAgIHdoZXJlIHR5cGUgaW4gKCdiJywgJ2gnLCAncCcsICd0JywgJ20nKQogICAgYW5kIHVwZGF0ZWQgPj0gc3RyZnRpbWUoJyVZJW0lZCVIJU0lUycsIGRhdGUoJ25vdycsICdsb2NhbHRpbWUnKSwgJ3N0YXJ0IG9mIGRheScpCiAgICBhbmQgdXBkYXRlZCA8IHN0cmZ0aW1lKCclWSVtJWQlSCVNJVMnLCBkYXRlKCdub3cnLCAnbG9jYWx0aW1lJyksICdzdGFydCBvZiBkYXknLCAnKzEgZGF5JykKICAgIGxpbWl0IDE7CmApOwpyZXR1cm4gY291bnRbMF0/LmNvdW50IHx8IDA7"} {: id="20250614064338-ur97jwo" updated="20250614082317"} 今日更新文字:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5LuK5pel77yI5pys5Zyw5pe26Ze077yaMDA6MDA6MDAg5YiwIDIzOjU5OjU577yJCiAgICBTRUxFQ1QgU1VNKElGTlVMTChMRU5HVEgoY29udGVudCksIDApKSBBUyBjb3VudCBGUk9NIGJsb2NrcwogICAgV0hFUkUgdHlwZSBpbiAoJ2InLCAnaCcsICdwJywgJ3QnLCAnbScpCiAgICBBTkQgdXBkYXRlZCA+PSBzdHJmdGltZSgnJVklbSVkJUglTSVTJywgZGF0ZSgnbm93JywgJ2xvY2FsdGltZScpLCAnc3RhcnQgb2YgZGF5JykKICAgIEFORCB1cGRhdGVkIDwgc3RyZnRpbWUoJyVZJW0lZCVIJU0lUycsIGRhdGUoJ25vdycsICdsb2NhbHRpbWUnKSwgJ3N0YXJ0IG9mIGRheScsICcrMSBkYXknKQogICAgTElNSVQgMTsKYCk7CnJldHVybiBjb3VudFswXT8uY291bnQgfHwgMDs="} {: id="20250614064338-844em5h" updated="20250614082422"} }}} {: id="20250614064403-52gxtxm" style="border:1px solid #888;padding:10px;" updated="20250614082422"} {{{row 本周更新文档:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5pys5ZGo77yI5pys5Zyw5pe26Ze077ya5pys5ZGo5LiAIDAwOjAwOjAwIOWIsCDmnKzlkajml6UgMjM6NTk6NTnvvIkKICAgIFNFTEVDVCBDT1VOVCgqKSBBUyBjb3VudAogICAgRlJPTSBibG9ja3MKICAgIFdIRVJFIHR5cGUgPSAnZCcKICAgIEFORCB1cGRhdGVkID49IHN0cmZ0aW1lKAogICAgICAgICclWSVtJWQwMDAwMDAnLAogICAgICAgIGRhdGV0aW1lKAogICAgICAgICAgICAnbm93JywgJ2xvY2FsdGltZScsCiAgICAgICAgICAgICctJyB8fCAoKHN0cmZ0aW1lKCcldycsICdub3cnLCAnbG9jYWx0aW1lJykgKyA2KSAlIDcpIHx8ICcgZGF5cycKICAgICAgICApCiAgICApCiAgICBBTkQgdXBkYXRlZCA8IHN0cmZ0aW1lKAogICAgICAgICclWSVtJWQwMDAwMDAnLAogICAgICAgIGRhdGV0aW1lKAogICAgICAgICAgICAnbm93JywgJ2xvY2FsdGltZScsCiAgICAgICAgICAgICctJyB8fCAoKHN0cmZ0aW1lKCcldycsICdub3cnLCAnbG9jYWx0aW1lJykgKyA2KSAlIDcpIHx8ICcgZGF5cycsCiAgICAgICAgICAgICcrNyBkYXlzJwogICAgICAgICkKICAgICkKICAgIGxpbWl0IDE7CmApOwpyZXR1cm4gY291bnRbMF0/LmNvdW50IHx8IDA7"} {: id="20250614064338-m75tnr5" updated="20250614081650"} 本周更新块:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5pys5ZGo77yI5pys5Zyw5pe26Ze077ya5pys5ZGo5LiAIDAwOjAwOjAwIOWIsCDmnKzlkajml6UgMjM6NTk6NTnvvIkKICAgIFNFTEVDVCBDT1VOVCgqKSBBUyBjb3VudAogICAgRlJPTSBibG9ja3MKICAgIFdIRVJFIHR5cGUgaW4gKCdiJywgJ2gnLCAncCcsICd0JywgJ20nKQogICAgQU5EIHVwZGF0ZWQgPj0gc3RyZnRpbWUoCiAgICAgICAgJyVZJW0lZDAwMDAwMCcsCiAgICAgICAgZGF0ZXRpbWUoCiAgICAgICAgICAgICdub3cnLCAnbG9jYWx0aW1lJywKICAgICAgICAgICAgJy0nIHx8ICgoc3RyZnRpbWUoJyV3JywgJ25vdycsICdsb2NhbHRpbWUnKSArIDYpICUgNykgfHwgJyBkYXlzJwogICAgICAgICkKICAgICkKICAgIEFORCB1cGRhdGVkIDwgc3RyZnRpbWUoCiAgICAgICAgJyVZJW0lZDAwMDAwMCcsCiAgICAgICAgZGF0ZXRpbWUoCiAgICAgICAgICAgICdub3cnLCAnbG9jYWx0aW1lJywKICAgICAgICAgICAgJy0nIHx8ICgoc3RyZnRpbWUoJyV3JywgJ25vdycsICdsb2NhbHRpbWUnKSArIDYpICUgNykgfHwgJyBkYXlzJywKICAgICAgICAgICAgJys3IGRheXMnCiAgICAgICAgKQogICAgKQogICAgbGltaXQgMTsKYCk7CnJldHVybiBjb3VudFswXT8uY291bnQgfHwgMDs="} {: id="20250614064338-91sfw5n" updated="20250614082406"} 本周更新文字:$Loading${: custom-js="Base64Text:bGV0IGNvdW50ID0gYXdhaXQgcXVlcnlTcWwoYAogICAgLS0g5Yy56YWN5pys5ZGo77yI5pys5Zyw5pe26Ze077ya5pys5ZGo5LiAIDAwOjAwOjAwIOWIsCDmnKzlkajml6UgMjM6NTk6NTnvvIkKICAgIFNFTEVDVCBTVU0oSUZOVUxMKExFTkdUSChjb250ZW50KSwgMCkpIEFTIGNvdW50CiAgICBGUk9NIGJsb2NrcwogICAgV0hFUkUgdHlwZSBpbiAoJ2InLCAnaCcsICdwJywgJ3QnLCAnbScpCiAgICBBTkQgdXBkYXRlZCA+PSBzdHJmdGltZSgKICAgICAgICAnJVklbSVkMDAwMDAwJywKICAgICAgICBkYXRldGltZSgKICAgICAgICAgICAgJ25vdycsICdsb2NhbHRpbWUnLAogICAgICAgICAgICAnLScgfHwgKChzdHJmdGltZSgnJXcnLCAnbm93JywgJ2xvY2FsdGltZScpICsgNikgJSA3KSB8fCAnIGRheXMnCiAgICAgICAgKQogICAgKQogICAgQU5EIHVwZGF0ZWQgPCBzdHJmdGltZSgKICAgICAgICAnJVklbSVkMDAwMDAwJywKICAgICAgICBkYXRldGltZSgKICAgICAgICAgICAgJ25vdycsICdsb2NhbHRpbWUnLAogICAgICAgICAgICAnLScgfHwgKChzdHJmdGltZSgnJXcnLCAnbm93JywgJ2xvY2FsdGltZScpICsgNikgJSA3KSB8fCAnIGRheXMnLAogICAgICAgICAgICAnKzcgZGF5cycKICAgICAgICApCiAgICApCiAgICBsaW1pdCAxOwpgKTsKcmV0dXJuIGNvdW50WzBdPy5jb3VudCB8fCAwOw=="} {: updated="20250614082448" id="20250614064338-pwff2zu"} }}} {: id="20250614064414-88zv4iq" style="border:1px solid #888;padding:10px;" updated="20250614082448"} }}} {: id="20250614064433-ugj0bfy" updated="20250614082448"}
生成统计图
条形统计图代码
if (typeof window.echarts === 'undefined') { // 创建 script 元素 const script = document.createElement('script'); script.src = window.siyuan ? '/stage/protyle/js/echarts/echarts.min.js?v=5.3.2' : 'https://unpkg.com/echarts@5.6.0/dist/echarts.min.js'; script.onload = function () { // 脚本加载完成后初始化图表 initChart(); }; document.head.appendChild(script); } else { // 如果已存在,直接初始化图表 initChart(); } function initChart() { // 基于准备好的dom,初始化echarts实例 const span = document.createElement('span'); span.style.width = '100px'; span.style.height = '24px'; span.style.display = 'inline-block'; element.innerHTML = ''; element.appendChild(span); notCheckEmptyTextContent(); var myChart = echarts.init(span); // 指定图表的配置项和数据 var option = { grid: { show: false, left: 0, right: 0, top: 0, bottom: 0 }, xAxis: { data: ['', '', '', '', '', ''], show: false, axisTick: { show: false }, axisLabel: { show: false } }, yAxis: { show: false }, series: [{ type: 'bar', barWidth: 5, // 5px 宽 barCategoryGap: 0, // 分类间距 0 data: [5, 20, 36, 10, 10, 20] }] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); }
折线图代码
if (typeof window.echarts === 'undefined') { // 创建 script 元素 const script = document.createElement('script'); script.src = window.siyuan ? '/stage/protyle/js/echarts/echarts.min.js?v=5.3.2' : 'https://unpkg.com/echarts@5.6.0/dist/echarts.min.js'; script.onload = function () { // 脚本加载完成后初始化图表 initChart(); }; document.head.appendChild(script); } else { // 如果已存在,直接初始化图表 initChart(); } function initChart() { // 基于准备好的dom,初始化echarts实例 const span = document.createElement('span'); span.style.width = '100px'; span.style.height = '24px'; span.style.display = 'inline-block'; element.innerHTML = ''; element.appendChild(span); notCheckEmptyTextContent(); var myChart = echarts.init(span); // 指定图表的配置项和数据 var option = { grid: { show: false, left: 0, right: 0, top: 0, bottom: 0 }, xAxis: { data: ['', '', '', '', '', ''], show: false, axisTick: { show: false }, axisLabel: { show: false } }, yAxis: { show: false }, series: [{ type: 'line', barWidth: 5, // 5px 宽 barCategoryGap: 0, // 分类间距 0 data: [5, 20, 36, 10, 10, 20] }] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); }
饼图代码
if (typeof window.echarts === 'undefined') { // 创建 script 元素 const script = document.createElement('script'); script.src = window.siyuan ? '/stage/protyle/js/echarts/echarts.min.js?v=5.3.2' : 'https://unpkg.com/echarts@5.6.0/dist/echarts.min.js'; script.onload = function () { // 脚本加载完成后初始化图表 initChart(); }; document.head.appendChild(script); } else { // 如果已存在,直接初始化图表 initChart(); } function initChart() { // 基于准备好的dom,初始化echarts实例 const span = document.createElement('span'); span.style.width = '24px'; span.style.height = '24px'; span.style.display = 'inline-block'; span.style.top = '4px'; element.innerHTML = ''; element.appendChild(span); notCheckEmptyTextContent(); var myChart = echarts.init(span); // 指定图表的配置项和数据 var option = { title: { show: false }, tooltip: { show: false }, legend: { show: false }, series: [ { type: 'pie', left: 0, right: 0, top: 0, bottom: 0, center: ['50%', '50%'], radius: ['0%', '100%'], data: [ { value: 1048, name: 'product1' }, { value: 735, name: 'product2' }, { value: 580, name: 'product3' }, { value: 484, name: 'product4' }, { value: 300, name: 'product5' } ], label: { show: false }, labelLine: { show: false }, // ↓ 这里是关键 ↓ emphasis: { // 1. 关掉默认的“放大”效果 scale: false, // 2. 定义 Hover 时的填充色(也可以用函数动态返回) itemStyle: { color: '#ff5722' // 比如变成橙色;不写也行,ECharts 会自动加深原色 } } } ] }; // 使用刚指定的配置项和数据显示图表。 myChart.setOption(option); }
以上仅抛转引玉, 更多功能等你发掘!
如果你有新的创意或杰作,欢迎在评论区分享!
复制和模板导入
查看全部行内 JS
使用 F 佬的“书签”插件,然后新建书签,选择动态书签,输入以下 SQL 即可
select * from blocks where markdown like '%custom-js="%' order by updated desc limit 500
多个行内 JS 协作
1)用 setShareData()把数据分享到全局
2)用 await getShareData()获取分享的数据
比如实现 C 行内 js 获取 A 和 B 行内 js 的数据可以这样实现
A 行内 js 代码如下
setShareData('a', 1); return 'A'
B 行内 js 代码如下
setShareData('b', 2); return 'B'
C 行内 js 代码如下
const a = await getShareData('a'); const b = await getShareData('b'); return a+b;
效果如下
外部使用行内 JS 数据
如果想在外部 js 代码中获取行内 js 共享的数据,可以通过 window.inlineJsShareData 获取。
仅运行一次
通常在日记模板中,你可能需要对插入的日期和天气保留当时的数据,而不是每次刷新都获取最新的数据。
这种情况下可以用以下方法实现:
1)renderOnce(element, result); 调用这个方法后,该方法即变为仅执行一次,刷新或修改代码都不会再执行,从模板导入时,仅第一次导入时执行。但,你可能有时需要让它重新执行,只需要注释该代码即恢复为正常执行方式,再次添加该方法又变为仅执行一次。
2)convertToTextForever(element, result); 顾名思义,该方法直接把返回结果转换为普通的文本结点,将不再是行内 js 元素,即仅运行一次后失效,运行后不可恢复,仅保留运行结果。
点击跳转
给 element 元素绑定 click 事件就行了,这里为了防止和点击弹出代码编辑框冲突,加了 ctrl/meta 按键。
代码
if (!element.clickHandle) { element.clickHandle = true; element.addEventListener('click', (event) => { if (event.ctrlKey || event.metaKey) { window.open('https://ld246.com'); } }); } return 'ctrl/meta + 点击 打开ld246.com';
调试
1)报错时会在行内 js 界面显示错误
2)devtools 控制台会显示错误
3)可以在行内代码中加入 debugger 进行断点调试
卸载
如果卸载本代码片段,并且不影响后续使用,可以按以下步骤操作
1)关闭或删除本代码片段
2)增加以下 css 代码即可,防止未删除 js 行内代码意外显示
.b3-typography span[data-type~=inline-math][custom-js], .protyle-wysiwyg span[data-type~=inline-math][custom-js] { display:none; }
3)如果想彻底删除 js 数据,可以通过以下 SQL 查询并删除
select * from blocks where markdown like '%custom-js="%' limit 999999;
外部编辑器编辑
也可以通过外部 IDE 进行编辑,可以使用以下方法(仅供参考)
1)用 fetch 获取
fetch('/yourcode.js') .then(res => res.text()) .then(jsCode => { // 把变量注入到 jsCode 中 const wrappedCode = ` (function(element, render, block){ ${jsCode} })(${JSON.stringify(element)}, ${JSON.stringify(render)}, ${JSON.stringify(block)})) `; const script = document.createElement('script'); script.textContent = wrappedCode; document.head.appendChild(script); });
这里只需要传入你需要的参数即可,然后更新行内 js 显示内容,可以用 render 函数。
2)约定函数名,比如
const script = document.createElement('script'); script.src = '/yourcode.js'; script.onload = () => { // 脚本加载后触发回调 if (window.inlineJsRun) { window.inlineJsRun({ element, render, block }); } }; document.head.appendChild(script);
这里只需要传入你需要的参数即可,然后更新行内 js 显示内容,可以用 render 函数。
3)用 import
import('/yourcode.js') .then(module => { // yourcode.js 里要有 `export default function(element, render, block){ ... }` module.default(element, render, block); }) .catch(err => { console.error('动态 import yourcode.js 失败', err); });
这里只需要传入你需要的参数即可,然后更新行内 js 显示内容,可以用 render 函数。
原理
行内 js 本质是通过行内公式改造的,用行内公式的好处是:
1)解决了行内元素删除和光标定位问题
2)解决了行内元素无法任意嵌套问题
已知问题
不可用 vscode 插件的 kramdown 编辑,编辑后会破坏行内代码数据。
不过,官方已经在积极修复了。
详见 issue Issue #15048 · siyuan-note/siyuan
及
issue Issue #37 · Zuoqiu-Yingyi/siyuan-plugin-monaco-editor
建议,把重要代码放到模板中或做好备份,即使不小心损坏了,也可以重新导入。
感谢作者
代码
👇 打赏后可见(为什么要打赏后可见?主要想通过这种方式统计使用人数及用户需求,以帮助作者分析哪些功能是用户最需要的)
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于