行内 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
建议,把重要代码放到模板中或做好备份,即使不小心损坏了,也可以重新导入。
感谢作者
代码
👇 打赏后可见(为什么要打赏后可见?主要想通过这种方式统计使用人数及用户需求,以帮助作者分析哪些功能是用户最需要的)
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于