+
74
-

回答

核心实现原理

监听选中事件:通过 CodeMirror 的 selectionChange 事件监听文本选中状态。

计算菜单位置:根据选中文本的坐标动态定位悬浮菜单。

渲染自定义菜单:创建 DOM 元素作为悬浮菜单,绑定交互事件。

完整代码

点击查看全文

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>CodeMirror 悬浮菜单</title>
<link type="text/css" rel="stylesheet" href="//repo.bfw.wiki/bfwrepo/css/codemirror.css">
  <style>
    /* 悬浮菜单样式 */
    .code-menu {
      position: absolute;
      background: white;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-shadow: 0 2px 8px rgba(0,0,0,0.1);
      z-index: 100;
      padding: 4px;
      display: none;
    }
    .code-menu button {
      display: block;
      width: 100%;
      padding: 6px 12px;
      background: none;
      border: none;
      text-align: left;
      cursor: pointer;
    }
    .code-menu button:hover {
      background: #f0f0f0;
    }
  </style>
</head>
<body>
  <textarea id="editor">// 选中代码试试看
  function hello() {
  console.log("Hello World");
  }</textarea>

<script type="text/javascript" src="//repo.bfw.wiki/bfwrepo/js/codemirror/codemirror.5.26.js"></script>
  <script>
 // 初始化编辑器
    const editor = CodeMirror.fromTextArea(document.getElementById('editor'), {
      lineNumbers: true,
      mode: 'javascript',
      value: '// 测试选中这段代码\nfunction test() { console.log("Hello"); }'
    });

    // 创建菜单
    const menu = document.createElement('div');
    menu.className = 'code-menu';
    document.body.appendChild(menu);

    // 菜单操作项
    ['复制', '注释', '格式化'].forEach(text => {
      const btn = document.createElement('button');
      btn.textContent = text;
      btn.style.display = 'block';
      btn.onclick = () => {
        if (text === '复制') handleCopy();
        if (text === '注释') handleComment();
        if (text === '格式化') handleFormat();
      };
      menu.appendChild(btn);
    });

    // 修复事件监听
    editor.on('cursorActivity', (cm) => {
      const selection = cm.getSelection();
      selection ? showMenu(cm) : hideMenu();
    });

    // 优化定位逻辑
    function showMenu(cm) {
      const cursor = cm.getCursor('to');
      const coords = cm.cursorCoords(cursor, 'page');
      const scroll = cm.getScrollInfo();
      
      menu.style.display = 'block';
      menu.style.top = `${coords.top + scroll.top - 15}px`; // 微调垂直位置
      menu.style.left = `${coords.right + scroll.left + 10}px`;
    }

    function hideMenu() {
      menu.style.display = 'none';
    }

    // 点击外部关闭
    document.addEventListener('click', (e) => {
      if (!menu.contains(e.target) && !cm.getWrapperElement().contains(e.target)) {
        hideMenu();
      }
    });

    // 示例操作函数
    function handleCopy() {
      const selection = editor.getSelection();
      navigator.clipboard.writeText(selection);
      hideMenu();
    }

    function handleComment() {
      const selection = editor.getSelection();
      editor.replaceSelection(`/* ${selection} */`);
    }

    function handleFormat() {
      const selection = editor.getSelection();
      // 此处可调用 Prettier 等格式化工具
      alert('格式化功能需集成格式化库');
    }
  </script>
</body>
</html>

网友回复

我知道答案,我要回答