[JS] 添加复选框按钮简单运行一些菜单项, 复制正文文字内容

移动安卓端的有些功能藏在菜单里,要划来划去.

就抄了个 js 代码片段放在页面两边,点击弹出对应菜单(太懒了).

不会 js,只能照板煮碗.

本来想抄个按钮代码的,不会改 >_<.

如果用 ai,估计能唰唰就完成了,可惜也不会.

20250316 更新:

一,PC 端在顶栏添加两个按钮,

一个是重载页面.

一个是半成品的复制正文文字内容.

由于个人能力问题,复制正文内容有以下缺点:

1.不知道怎样定位之前打开的页面,只能复制最新打开的页面.如果想复制已打开的页面,请关闭它再重新打开.

2.复制的正文文字内容只能复制已加载的部分,太长的内容由于懒加载原因而未加载的部分请手动加载后再复制.

二,安卓端其中一个按钮也更改为半成品的复制正文文字内容


原代码:

[js] [仅适用于手机端] 一个在锁定编辑旁边可以隐藏一些图标的 js


相关注意项:


1.右边复选框开关我点击的菜单是右边的菜单第一层,

可以自行修改为想运行的代码或相关菜单的 id.

如:#plugin_siyuan-plugins-index_0


2.左边复选框开关我点击的是左边顶层工具栏的插件工具栏里面的一项.

找不到菜单第一层和第二层的 id 和类名,只好用最粗旷的模拟点击.

这个就和主题的按钮大小相关了.

默认主题按钮大概是 43px,

所以插件工具栏(第七个)的 Y 坐标取 280.

43*6=258, 43*7=301

需要修改为你自己想点击的菜单的坐标.

当然,各位大佬直接修改为想运行的代码更直接.


3.坐标也可以用这个小代码来获取.

打开思源浏览器安卓端,按下 F12,在控制台粘贴以下代码,按 Enter

然后点击网页你想点击的地方,就可以得到坐标, ID(如果有)和类名(如果有).

document.addEventListener('click', (e) => {
  console.log(e.target);
// 获取点击的坐标
    const x = e.clientX;
    const y = e.clientY;

    // 输出坐标
    console.log(`坐标: (${x}, ${y})`);
});

4.左边工具栏第二层菜单生成需要时间,我添加了延时,默认 1500.

你可以根据自己手机性能来调整大小.

第三次点击是根据菜单名字搜索的,

这条代码由 JeffreyChen 大大 请问类名相同, 如何根据文本内容获取元素, 谢谢. 提供,谢谢.

var button003 = Array.from(document.querySelectorAll('span.b3-menu__label')).find(label => label.textContent.includes('基于文档搜索 Dock'));

可以修改自己想点击的菜单名字

如"基于文档搜索 Dock"


5.上面 1,2,4 点在代码里面都有注释.


PC 端复制正文文字内容录屏 gif:

screenshots.gif


浏览器移动端录屏 gif:

移动端弹出菜单复制正文.gif




20250316 新版代码:


// 插入开关功能
function insertSwitchBeforeButton() {
  // 循环查找目标按钮,直到找到为止
  function findAndInsert() {
    // 尝试查找目标按钮,仅在符合指定选择器时插入
    let barforward_desktop = document.querySelector("#barForward");
    let drag_desktop = document.querySelector("#drag");

    let spacebar_mobile = document.querySelector('#editor > div.protyle-breadcrumb > span.protyle-breadcrumb__space');
    let unlock_mobile = document.querySelector('#editor > div.protyle-breadcrumb > button.protyle-breadcrumb__icon.ariaLabel');
    let toolbar = document.querySelector('.toolbar.toolbar--border'); // 查找需要控制的工具栏


    let css1 = document.querySelector('#editor > div.protyle-content.protyle-content--transition > div.protyle-top');


    if ( barforward_desktop || spacebar_mobile ) {
      // 如果找到了目标按钮和工具栏,且符合选择器
      let switchElement_desktop01 = document.createElement('input');
      switchElement_desktop01.type = 'checkbox'; // 创建一个复选框作为开关
      switchElement_desktop01.style.marginRight = '8px'; // 添加样式,调整位置
      switchElement_desktop01.checked = false; // 默认是未选中状态,工具栏显示

      let switchElement_desktop02 = document.createElement('input');
      switchElement_desktop02.type = 'checkbox'; // 创建一个复选框作为开关01
      switchElement_desktop02.style.marginLeft = '8px'; // 添加样式,调整位置
      switchElement_desktop02.checked = false; // 默认是未选中状态,工具栏显示

      let switchElement_mobile01 = document.createElement('input');
      switchElement_mobile01.type = 'checkbox'; // 创建一个复选框作为开关
      switchElement_mobile01.style.marginRight = '8px'; // 添加样式,调整位置
      switchElement_mobile01.checked = false; // 默认是未选中状态,工具栏显示

      let switchElement_mobile02 = document.createElement('input');
      switchElement_mobile02.type = 'checkbox'; // 创建一个复选框作为开关01
      switchElement_mobile02.style.marginLeft = '8px'; // 添加样式,调整位置
      switchElement_mobile02.checked = false; // 默认是未选中状态,工具栏显示


      // 桌面端开关01的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement_desktop01.addEventListener('change', () => {
        if (switchElement_desktop01.checked) {
          //下面可以修改为想运行的代码
          copyAreaContent();
          //上面是想运行的代码
          setTimeout(() => {
            switchElement_desktop01.checked = false;
          }, 500);
        }
      });

      // 桌面端开关02的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement_desktop02.addEventListener('change', () => {
        if (switchElement_desktop02.checked) {
          //下面可以修改为想运行的代码
          refresh01();
          //上面是想运行的代码
          setTimeout(() => {
            switchElement_desktop02.checked = false;
          }, 500);
        }
      });


      // 移动端开关01的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement_mobile01.addEventListener('change', () => {
        if (switchElement_mobile01.checked) {
          //下面可以修改为想运行的代码
          copyAreaContent();
          //上面是想运行的代码
          setTimeout(() => {
            switchElement_mobile01.checked = false;
          }, 500);
        }
      });

      // 移动端开关02的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement_mobile02.addEventListener('change', () => {
        if (switchElement_mobile02.checked) {
          //下面可以修改为想运行的代码
          small_notes_sidebar();
          //上面是想运行的代码
          setTimeout(() => {
            switchElement_mobile02.checked = false;
          }, 500);
        }
      });


      // 插入开关到按钮前面
      if (barforward_desktop) {
      // 桌面端
      barforward_desktop.parentNode.insertBefore(switchElement_desktop01, drag_desktop);
      console.log("桌面端开关01已成功插入到目标按钮前面");
      barforward_desktop.parentNode.insertBefore(switchElement_desktop02, barforward_desktop);
      console.log("桌面端开关02已成功插入到目标按钮前面");
      } else {
      // 移动端
      spacebar_mobile.parentNode.insertBefore(switchElement_mobile01, unlock_mobile);
      console.log("移动端开关01已成功插入到目标按钮前面");
      spacebar_mobile.parentNode.insertBefore(switchElement_mobile02, spacebar_mobile);
      console.log("移动端开关02已成功插入到目标按钮前面");
      }


      // 停止查找
      clearInterval(searchInterval);
    } else {
      console.warn("目标按钮或工具栏未找到,继续尝试...");
    }
  }


  // 每隔 1000 毫秒查找一次目标按钮和工具栏
  let searchInterval = setInterval(findAndInsert, 1000);


  // 如果超过 10 秒仍未找到,停止查找并提示
  setTimeout(() => {
    clearInterval(searchInterval);
    console.error("超过 2 秒仍未找到目标按钮或工具栏,请检查选择器或页面是否正确加载");
  }, 2000); // 2 秒后停止查找
}


// 启动插入开关功能
insertSwitchBeforeButton();

//以下是点击按钮实现的4个效果
//效果1,重置刷新页面
function refresh01() {
  //重置刷新页面
  window.location.reload();
}

//效果2,小记弹出新建窗口(安卓端)
function small_notes() {
  这里是点击右边的顶层菜单项
  window.location.reload(); //这个是重置页面
   获取元素
  let button001 = document.querySelector('#plugin_quick-notes_0');
   触发点击事件
  button001.click();
  console.log("小记弹出新建窗口成功");
}

//效果3,显示小记侧边栏(安卓端)
function small_notes_sidebar() {
  //这里是运行左边顶部工具栏插件按钮第二层的显示侧边栏
  //第二次点击的坐标x2,y2要根据主题来修改
  // 定义我们要点击的坐标1
  const x1 = 10;
  const y1 = 10;
  
    // 获取指定坐标上的元素
    const el = document.elementFromPoint(x1, y1);
  
  const click01 = (x1, y1) => {
    const ev = new MouseEvent('click', {
      'view': window,
      'bubbles': true,
      'cancelable': true,
      'screenX': x1,
      'screenY': y1
    });
  
    // 分发点击事件
    el.dispatchEvent(ev);
  };
  
  // 调用点击函数
  click01(x1, y1);
  
  console.log(el);
  
  
  setTimeout(() => {
    // 定义我们要点击的坐标2
    const x2 = 260; //默认主题顶部工具栏插件按钮的坐标为280
    const y2 = 20;
  
      // 获取指定坐标上的元素
      const e2 = document.elementFromPoint(x2, y2);
  
    const click02 = (x2, y2) => {
      const ev = new MouseEvent('click', {
        'view': window,
        'bubbles': true,
        'cancelable': true,
        'screenX': x2,
        'screenY': y2
      });
  
      // 分发点击事件
      e2.dispatchEvent(ev);
    };
  
    // 调用点击函数
    click02(x2, y2);
  
    console.log(e2);
  }, 500);
  
  setTimeout(() => {
    var button003 = Array.from(document.querySelectorAll('span.b3-menu__label')).find(label => label.textContent.includes('显示小记侧边栏'));
    button003.click();
    console.log("小记侧边栏已显示");
  }, 1500); //这里的延迟时间看手机性能,性能不够的可以调大一些
}

//效果4,半成品复制正文文字
function copyAreaContent() {
  // 1. 获取目标元素
  let targetElement_mobile = document.querySelector('#editor > div.protyle-content.protyle-content--transition > div.protyle-wysiwyg.protyle-wysiwyg--attr');
  let targetElement_desktop = Array.from(document.getElementsByClassName('protyle-wysiwyg--attr'));
  let i001 = targetElement_desktop.length-1;
  let targetElement = (targetElement_desktop[i001] || targetElement_mobile);
  
  // 2. 创建选区(Range)
  const range = document.createRange();
  range.selectNodeContents(targetElement);
  
  // 3. 将选区添加到 Selection 对象
  const selection = window.getSelection();
  selection.removeAllRanges(); // 清除现有选区
  selection.addRange(range);
  
  try {
    // 4. 复制内容到剪贴板(现代浏览器推荐 Clipboard API)
    //if (navigator.clipboard) {
       let textArea = document.createElement("textarea");
       str01 = selection.toString().replace(//g,"");
       textArea.value = str01;
       // 使text area不在viewport,同时设置不可见
       textArea.style.position = "absolute";
       textArea.style.opacity = 0;
       textArea.style.left = "-999999px";
       textArea.style.top = "-999999px";
       document.body.appendChild(textArea);
       textArea.focus();
       textArea.select();
       return new Promise((res, rej) => {
           // 执行复制命令并移除文本框
           document.execCommand('copy') ? res() : rej();
           textArea.remove();
           console.log("内容已复制");
       });
    //} else {
    //  // 旧浏览器回退到 execCommand
    //  document.execCommand("copy");
    //  console.log("内容已复制(旧浏览器方式)");
    //}
  } catch (err) {
    console.error("复制失败:", err);
  }
  
  // 5. 清除选区(可选)
  selection.removeAllRanges();
}

旧版代码:


// 插入开关功能
function insertSwitchBeforeButton() {
  // 循环查找目标按钮,直到找到为止
  function findAndInsert() {
    // 尝试查找目标按钮,仅在符合指定选择器时插入
    let targetButton = document.querySelector('#editor > div.protyle-breadcrumb > button.protyle-breadcrumb__icon.ariaLabel');
    let targetButton01 = document.querySelector('#editor > div.protyle-breadcrumb > span.protyle-breadcrumb__space');
    let toolbar = document.querySelector('.toolbar.toolbar--border'); // 查找需要控制的工具栏


    let css1 = document.querySelector('#editor > div.protyle-content.protyle-content--transition > div.protyle-top');


    if (targetButton && toolbar) {
      // 如果找到了目标按钮和工具栏,且符合选择器
      let switchElement = document.createElement('input');
      switchElement.type = 'checkbox'; // 创建一个复选框作为开关
      switchElement.style.marginRight = '8px'; // 添加样式,调整位置
      switchElement.checked = false; // 默认是未选中状态,工具栏显示


      let switchElement01 = document.createElement('input');
      switchElement01.type = 'checkbox'; // 创建一个复选框作为开关01
      switchElement01.style.marginLeft = '8px'; // 添加样式,调整位置
      switchElement01.checked = false; // 默认是未选中状态,工具栏显示


      // 开关的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement.addEventListener('change', () => {
        if (switchElement.checked) {
          //下面可以修改为想运行的代码
          //这里是点击右边的顶层菜单项
          //window.location.reload(); //这个是重置页面
          // 获取元素
          let button001 = document.querySelector('#plugin_siyuan-plugins-index_0');//修改为想点击的菜单id
          // 触发点击事件
          button001.click();
          //上面是想运行的代码
          switchElement.checked = false;
          console.log("文档目录已更新");
        }
      });


      // 开关01的点击事件,用来控制工具栏和按钮的显示和隐藏
      switchElement01.addEventListener('change', () => {
        if (switchElement01.checked) {
          //下面可以修改为想运行的代码
          //这里是运行左边顶部工具栏插件按钮第二层的显示侧边栏
          //第二次点击的坐标x2,y2要根据主题来修改
          // 定义我们要点击的坐标1
          const x1 = 10;
          const y1 = 10;
  
            // 获取指定坐标上的元素
            const el = document.elementFromPoint(x1, y1);
  
          const click01 = (x1, y1) => {
            const ev = new MouseEvent('click', {
              'view': window,
              'bubbles': true,
              'cancelable': true,
              'screenX': x1,
              'screenY': y1
            });
  
            // 分发点击事件
            el.dispatchEvent(ev);
          };
  
          // 调用点击函数
          click01(x1, y1);
  
          console.log(el);
  
  
          setTimeout(() => {
            // 定义我们要点击的坐标2
            const x2 = 280; //默认主题顶部工具栏插件按钮的坐标为280
            const y2 = 20;
  
              // 获取指定坐标上的元素
              const e2 = document.elementFromPoint(x2, y2);
  
            const click02 = (x2, y2) => {
              const ev = new MouseEvent('click', {
                'view': window,
                'bubbles': true,
                'cancelable': true,
                'screenX': x2,
                'screenY': y2
              });
  
              // 分发点击事件
              e2.dispatchEvent(ev);
            };
  
            // 调用点击函数
            click02(x2, y2);
  
            console.log(e2);
          }, 500);
  
          setTimeout(() => {
            var button003 = Array.from(document.querySelectorAll('span.b3-menu__label')).find(label => label.textContent.includes('基于文档搜索 Dock'));
            button003.click();
          }, 1500); //这里的延迟时间看手机性能,性能不够的可以调大一些
          //上面是想运行的代码
          switchElement01.checked = false;
          console.log("文档搜索");
        }
      });


      // 插入开关到按钮前面
      targetButton.parentNode.insertBefore(switchElement, targetButton);
      console.log("开关已成功插入到目标按钮前面");
      targetButton01.parentNode.insertBefore(switchElement01, targetButton01);
      console.log("开关01已成功插入到目标按钮前面");


      // 停止查找
      clearInterval(searchInterval);
    } else {
      console.warn("目标按钮或工具栏未找到,继续尝试...");
    }
  }


  // 每隔 1000 毫秒查找一次目标按钮和工具栏
  let searchInterval = setInterval(findAndInsert, 1000);


  // 如果超过 10 秒仍未找到,停止查找并提示
  setTimeout(() => {
    clearInterval(searchInterval);
    console.error("超过 2 秒仍未找到目标按钮或工具栏,请检查选择器或页面是否正确加载");
  }, 2000); // 2 秒后停止查找
}


// 启动插入开关功能
insertSwitchBeforeButton();
  • 思源笔记

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

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

    25512 引用 • 105500 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    733 引用 • 1280 回帖

相关帖子

欢迎来到这里!

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

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

推荐标签 标签

  • 自由行
  • 友情链接

    确认过眼神后的灵魂连接,站在链在!

    24 引用 • 373 回帖 • 1 关注
  • Office

    Office 现已更名为 Microsoft 365. Microsoft 365 将高级 Office 应用(如 Word、Excel 和 PowerPoint)与 1 TB 的 OneDrive 云存储空间、高级安全性等结合在一起,可帮助你在任何设备上完成操作。

    5 引用 • 34 回帖
  • JavaScript

    JavaScript 一种动态类型、弱类型、基于原型的直译式脚本语言,内置支持类型。它的解释器被称为 JavaScript 引擎,为浏览器的一部分,广泛用于客户端的脚本语言,最早是在 HTML 网页上使用,用来给 HTML 网页增加动态功能。

    733 引用 • 1280 回帖
  • Thymeleaf

    Thymeleaf 是一款用于渲染 XML/XHTML/HTML5 内容的模板引擎。类似 Velocity、 FreeMarker 等,它也可以轻易的与 Spring 等 Web 框架进行集成作为 Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个 Web 应用。

    11 引用 • 19 回帖 • 392 关注
  • GraphQL

    GraphQL 是一个用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由你的数据定义)。GraphQL 并没有和任何特定数据库或者存储引擎绑定,而是依靠你现有的代码和数据支撑。

    4 引用 • 3 回帖 • 13 关注
  • Bug

    Bug 本意是指臭虫、缺陷、损坏、犯贫、窃听器、小虫等。现在人们把在程序中一些缺陷或问题统称为 bug(漏洞)。

    76 引用 • 1742 回帖 • 7 关注
  • ZeroNet

    ZeroNet 是一个基于比特币加密技术和 BT 网络技术的去中心化的、开放开源的网络和交流系统。

    1 引用 • 21 回帖 • 649 关注
  • React

    React 是 Facebook 开源的一个用于构建 UI 的 JavaScript 库。

    192 引用 • 291 回帖 • 373 关注
  • DNSPod

    DNSPod 建立于 2006 年 3 月份,是一款免费智能 DNS 产品。 DNSPod 可以为同时有电信、网通、教育网服务器的网站提供智能的解析,让电信用户访问电信的服务器,网通的用户访问网通的服务器,教育网的用户访问教育网的服务器,达到互联互通的效果。

    6 引用 • 26 回帖 • 530 关注
  • 996
    13 引用 • 200 回帖 • 8 关注
  • CSDN

    CSDN (Chinese Software Developer Network) 创立于 1999 年,是中国的 IT 社区和服务平台,为中国的软件开发者和 IT 从业者提供知识传播、职业发展、软件开发等全生命周期服务,满足他们在职业发展中学习及共享知识和信息、建立职业发展社交圈、通过软件开发实现技术商业化等刚性需求。

    14 引用 • 155 回帖
  • TGIF

    Thank God It's Friday! 感谢老天,总算到星期五啦!

    290 引用 • 4494 回帖 • 653 关注
  • Notion

    Notion - The all-in-one workspace for your notes, tasks, wikis, and databases.

    10 引用 • 77 回帖
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    227 引用 • 476 回帖
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    86 引用 • 165 回帖 • 4 关注
  • C++

    C++ 是在 C 语言的基础上开发的一种通用编程语言,应用广泛。C++ 支持多种编程范式,面向对象编程、泛型编程和过程化编程。

    107 引用 • 153 回帖
  • PWA

    PWA(Progressive Web App)是 Google 在 2015 年提出、2016 年 6 月开始推广的项目。它结合了一系列现代 Web 技术,在网页应用中实现和原生应用相近的用户体验。

    14 引用 • 69 回帖 • 175 关注
  • OAuth

    OAuth 协议为用户资源的授权提供了一个安全的、开放而又简易的标准。与以往的授权方式不同之处是 oAuth 的授权不会使第三方触及到用户的帐号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就可以申请获得该用户资源的授权,因此 oAuth 是安全的。oAuth 是 Open Authorization 的简写。

    36 引用 • 103 回帖 • 30 关注
  • NetBeans

    NetBeans 是一个始于 1997 年的 Xelfi 计划,本身是捷克布拉格查理大学的数学及物理学院的学生计划。此计划延伸而成立了一家公司进而发展这个商用版本的 NetBeans IDE,直到 1999 年 Sun 买下此公司。Sun 于次年(2000 年)六月将 NetBeans IDE 开源,直到现在 NetBeans 的社群依然持续增长。

    78 引用 • 102 回帖 • 701 关注
  • RabbitMQ

    RabbitMQ 是一个开源的 AMQP 实现,服务器端用 Erlang 语言编写,支持多种语言客户端,如:Python、Ruby、.NET、Java、C、PHP、ActionScript 等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。

    49 引用 • 60 回帖 • 344 关注
  • IPFS

    IPFS(InterPlanetary File System,星际文件系统)是永久的、去中心化保存和共享文件的方法,这是一种内容可寻址、版本化、点对点超媒体的分布式协议。请浏览 IPFS 入门笔记了解更多细节。

    21 引用 • 245 回帖 • 228 关注
  • 安装

    你若安好,便是晴天。

    132 引用 • 1184 回帖 • 3 关注
  • CAP

    CAP 指的是在一个分布式系统中, Consistency(一致性)、 Availability(可用性)、Partition tolerance(分区容错性),三者不可兼得。

    12 引用 • 5 回帖 • 630 关注
  • CongSec

    本标签主要用于分享网络空间安全专业的学习笔记

    1 引用 • 1 回帖 • 31 关注
  • 智能合约

    智能合约(Smart contract)是一种旨在以信息化方式传播、验证或执行合同的计算机协议。智能合约允许在没有第三方的情况下进行可信交易,这些交易可追踪且不可逆转。智能合约概念于 1994 年由 Nick Szabo 首次提出。

    1 引用 • 11 回帖 • 2 关注
  • CodeMirror
    2 引用 • 17 回帖 • 162 关注