用一个 Bookmarklet(书签脚本),给任意网页挂一个可拖拽悬浮窗

9 阅读4分钟

用一个 Bookmarklet(书签脚本),给任意网页挂一个可拖拽悬浮窗

最近写了一个挺有意思的小工具。

只要点击一下浏览器书签,就能在当前网页右下角弹出一个小窗口直接打开另一个网站。

而且支持:

  • 拖拽移动
  • 折叠
  • 关闭
  • iframe 内嵌网页

核心代码大概长这样:

javascript:(()=>{
  const IFRAME_URL='https://ruankaodaren.com';

  const WIDTH=520,HEIGHT=300;

  if(document.getElementById('__mini_window__')) return;

  const box=document.createElement('div');

  box.id='__mini_window__';

  Object.assign(box.style,{
    position:'fixed',
    right:'24px',
    bottom:'24px',
    width:WIDTH+'px',
    height:HEIGHT+'px',
    background:'#fff',
    borderRadius:'10px',
    boxShadow:'0 10px 30px rgba(0,0,0,.12)',
    zIndex:999999,
    overflow:'hidden',
    fontFamily:'system-ui,-apple-system'
  });

  const header=document.createElement('div');

  Object.assign(header.style,{
    height:'32px',
    display:'flex',
    alignItems:'center',
    justifyContent:'flex-end',
    padding:'0 8px',
    borderBottom:'1px solid #eee',
    cursor:'move',
    userSelect:'none'
  });

  const mkBtn=t=>{
    const b=document.createElement('button');

    b.innerText=t;

    Object.assign(b.style,{
      width:'28px',
      height:'28px',
      border:'none',
      background:'transparent',
      cursor:'pointer',
      color:'#666',
      fontSize:'16px'
    });

    return b;
  };

  const toggle=mkBtn('–'),
        close=mkBtn('×');

  header.append(toggle,close);

  const iframe=document.createElement('iframe');

  iframe.src=IFRAME_URL;

  Object.assign(iframe.style,{
    width:'100%',
    height:'calc(100% - 32px)',
    border:'none'
  });

  box.append(header,iframe);

  document.body.appendChild(box);

  let collapsed=false;

  toggle.onclick=()=>{
    collapsed=!collapsed;

    box.style.height=collapsed?'32px':HEIGHT+'px';

    toggle.innerText=collapsed?'+':'–';
  };

  close.onclick=()=>box.remove();

  let dragging=false,sx,sy,sl,st;

  header.onmousedown=e=>{
    dragging=true;

    sx=e.clientX;
    sy=e.clientY;

    const r=box.getBoundingClientRect();

    sl=r.left;
    st=r.top;

    document.body.style.userSelect='none';
  };

  document.onmousemove=e=>{
    if(!dragging)return;

    box.style.left=sl+e.clientX-sx+'px';

    box.style.top=st+e.clientY-sy+'px';

    box.style.right='auto';

    box.style.bottom='auto';
  };

  document.onmouseup=()=>{
    dragging=false;

    document.body.style.userSelect='';
  };
})();

什么是 Bookmarklet(书签脚本)

很多人第一次看到:

javascript:(()=>{})()

都会有点懵。

其实它本质上就是:

把 JavaScript 当成浏览器书签执行。

也就是说:

普通书签:

https://google.com

会跳转网页。

但 Bookmarklet:

javascript:alert('hello')

点击后会直接执行 JS。

所以它能做很多有意思的事情:

  • 自动填写表单
  • 网页增强
  • 页面调试
  • 注入工具
  • 快捷操作
  • 网页悬浮助手

很多年前流行的玩法了。


这个小工具到底做了什么

整体逻辑其实很简单:

创建一个悬浮 div
    ↓
顶部做控制栏
    ↓
里面塞 iframe
    ↓
支持拖拽 / 折叠 / 关闭

最终效果有点像:

  • 在线客服
  • AI 助手
  • 小窗播放器
  • 浏览器侧边工具

这种悬浮 UI。

效果展示:

image.png


iframe 才是整个核心

真正关键的是:

iframe.src=IFRAME_URL;

它意味着:

在当前网页里,再打开另一个网页。

于是你就能实现:

  • 网页里的 AI 助手
  • 小窗文档
  • 内部系统快捷入口
  • 在线翻译
  • 调试工具

甚至可以做:

“网页中的网页”

这也是 iframe 一直到今天还没被淘汰的原因。


拖拽功能其实很有意思

很多人第一次实现拖拽时都会发现:

原来拖拽本质上只是:
“计算鼠标移动距离”

代码核心其实就两步:

记录鼠标按下的位置:

sx=e.clientX;
sy=e.clientY;

然后移动时不断更新:

box.style.left=
  sl+e.clientX-sx+'px';

于是元素就“跟着鼠标走”了。

很多 UI 框架里的:

  • 弹窗拖拽
  • 看板拖拽
  • 编辑器拖拽

本质都是这一套。


为什么这种代码很适合练前端基础

因为它没有框架帮你处理。

你会真实接触:

  • DOM
  • 事件
  • 坐标
  • 定位
  • iframe
  • 浏览器行为

这些才是真正的浏览器底层能力。

很多人用 Vue、React 久了。

会逐渐忘记:

前端最终操作的其实一直都是 DOM。


这种东西能拿来干什么

其实用途非常多。

比如:

AI 助手

随时悬浮一个 ChatGPT。


内部系统快捷入口

不用切 Tab。


在线翻译

选中网页直接查。


接口调试

直接小窗打开 Swagger。


开发工具

甚至可以做:

  • JSON 格式化
  • 代码运行器
  • 页面检查工具

但它也有局限性

不是所有网站都能 iframe 打开。

有些网站会主动禁止:

X-Frame-Options

或者:

Content-Security-Policy

这时候 iframe 会直接失效。

所以这种方案:

更适合:

  • 自己的网站
  • 内部系统
  • 可控页面

最后

我一直觉得:

Bookmarklet 是一种很有意思的前端玩法。

它不复杂。

甚至代码量很小。

但你会非常直观地感受到:

浏览器 ≠ 网页浏览工具

浏览器其实本身就是一个运行环境。

而前端开发。

很多时候其实就是:

想办法让浏览器变得更有趣。

这个功能好像缺了点什么?缩放和快捷键?