[js] 行内 js,一切皆有可能

行内 js,顾名思义,就是在块内插入可执行的 js 代码。

比较适用于在行内执行一些简单的 js,比如获取天气,获取当前日期和时间,生成倒计时,数据统计等。

效果

r143.gif

使用方法

注意:需要在设置-> 编辑器配置中,开启 Markdown 行级公式语法 感谢 @Fighter93 提醒!

内置函数使用手册

函数内内置了一些变量和方法,比如:

elementel 行内 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: 当前文档的 ID
    • protyle.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: 返回格式 (jsontext)
  • 返回值: 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 到元素

使用示例

获取天气

image.png

const weather = await fetch('https://wttr.in/?format=1'); const text = await weather.text(); return "今日天气:" + text.trim();

获取当前日期和时间

image.png

return date.datetime;

生成倒计时

image.png

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");

颜色渐变动画

r144.gif

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';

打字机效果

r145.gif

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; // 返回已打完的文本

数据统计

image.png

把下面代码粘贴到模板或文档中即可

{{{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"}

生成统计图

image.png

条形统计图代码

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); }

以上仅抛转引玉, 更多功能等你发掘!

如果你有新的创意或杰作,欢迎在评论区分享!

复制和模板导入

image.png

查看全部行内 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;

效果如下

image.png

外部使用行内 JS 数据

如果想在外部 js 代码中获取行内 js 共享的数据,可以通过 window.inlineJsShareData 获取。

仅运行一次

通常在日记模板中,你可能需要对插入的日期和天气保留当时的数据,而不是每次刷新都获取最新的数据。

这种情况下可以用以下方法实现:

1)renderOnce(element, result); 调用这个方法后,该方法即变为仅执行一次,刷新或修改代码都不会再执行,从模板导入时,仅第一次导入时执行。但,你可能有时需要让它重新执行,只需要注释该代码即恢复为正常执行方式,再次添加该方法又变为仅执行一次。

2)convertToTextForever(element, result); 顾名思义,该方法直接把返回结果转换为普通的文本结点,将不再是行内 js 元素,即仅运行一次后失效,运行后不可恢复,仅保留运行结果。

点击跳转

给 element 元素绑定 click 事件就行了,这里为了防止和点击弹出代码编辑框冲突,加了 ctrl/meta 按键。

image.png

代码

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 界面显示错误

image.png

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

建议,把重要代码放到模板中或做好备份,即使不小心损坏了,也可以重新导入。

感谢作者

image.png

代码

👇 打赏后可见(为什么要打赏后可见?主要想通过这种方式统计使用人数及用户需求,以帮助作者分析哪些功能是用户最需要的)

打赏 30 积分后可见
30 积分 • 20 打赏
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    26014 引用 • 107964 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    186 引用 • 1314 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • drawone

    编辑器上面的按钮里面应该自带预设的常用模板

    1 回复
  • 其他回帖
  • wilsons

    哈哈,好建议,但我也不知道常用模板都有哪些?

    我能想到的都写到示例里了 使用示例

  • Fighter93

    在我的旧工作空间仍然不起作用的:

    1、代码已是最新版

    1.PNG

    2、代码正确安装,已打开开关

    3、重启几次都是一样

    4、控制台报错了,错误位置是第 984 行代码,如图:

    4.PNG

    5.PNG

    5、我的 js 代码只有这一段

    6、录屏,如图:

    screenshots.gif

    总结:在我的新建的工作空间起效了,可能是我旧工作空间某个未考虑到的因素导致的。

    1 回复
    1 操作
    Fighter93 在 2025-06-18 13:10:30 更新了该回帖
  • Fighter93 1

    原因找到了,需要在软件设置中把“Markdown 行级公式语法打开”,如图:

    捕获.PNG

    你需要把这个影响到代码运行的因素写在文章、Gitee 开源页面等处。

    1 回复
  • 查看全部回帖