如何使用 Selection API 创建选中文本的操作

Avatar of Preethi
Preethi

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 200 美元的免费额度!

点击、拖动、释放:您刚刚在网页上选中了一些文本——可能是为了复制粘贴到其他地方或分享。 如果选中该文本后出现一些使这些任务更容易的操作,那不是很酷吗? 这就是选择菜单的作用。

如果您曾经使用过在线编辑器,您可能已经熟悉选择菜单了。 当您选择文本时,格式化所选内容的选项可能会浮在其上方。 事实上,我正在使用一个执行此操作的编辑器撰写此草稿。

让我们看看如何使用 JavaScript 的 Selection API 创建这样的选择菜单。 该 API 使我们能够访问网页上选中区域的空间和内容。 通过这种方式,我们可以将选择菜单正好放置在选中文本的上方,并访问选定的文本本身。

这是一个包含一些示例文本的 HTML 代码片段

<article>
  <h1>Select over the text below</h1> 
  <p>Cascading Style Sheets (CSS) is a style sheet language used for describing the presentation of a document written in a markup language such as HTML. CSS is a cornerstone technology of the World Wide Web, alongside HTML and JavaScript. CSS is designed to enable the separation of presentation and content, including layout, colors, and fonts. This separation can improve content accessibility, provide more flexibility and control in the specification of presentation characteristics. </p>
</article>
<template><span id="control"></span></template>

最后有一个<template>标签。 <span>内部是我们的选择菜单控件。 <template>标签内的任何内容都不会在页面上渲染,除非稍后使用 JavaScript 将其添加到页面中。 当用户选择文本时,我们将选择菜单控件添加到页面中。 并且当用户选择该文本时,我们的选择菜单将提示用户将其发布到 Twitter。

这是用于设置样式的 CSS 代码

#control {
    background-image: url("data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 width='40px' height='40px'><foreignObject width='40px' height='40px'><div xmlns='http://www.w3.org/1999/xhtml' style='width:40px;height:40px;line-height:40px;text-align:center;color:transparent;text-shadow: 0 0 yellow, 2px 4px black, -1px -1px black;font-size:35px;'>💬</div></foreignObject></svg>");
  cursor: pointer;
  position: absolute;
  width: 40px;
  height: 40px;
}
#control::before{
  background-color: black;
  color: white;
  content: " tweet this! ";
  display: block;
  font-weight: bold;
  margin-left: 37px;
  margin-top: 6px;
  padding: 2px;
  width: max-content;
  height: 20px;
}

查看这篇文章 文章,了解我如何使用表情符号 (💬) 作为背景图像。

到目前为止,示例文本已准备就绪,并且选择菜单控件已设置样式。 让我们继续 JavaScript 部分。 当进行选择时,我们将获取页面上选中区域的大小和位置。 然后,我们使用这些测量结果将选择菜单控件的位置分配到选中区域的顶部中间。

var control = document.importNode(document.querySelector('template').content, true).childNodes[0];
document.querySelector('p').onpointerup = () => {
  let selection = document.getSelection(), text = selection.toString();
  if (text !== "") {
    let rect = selection.getRangeAt(0).getBoundingClientRect();
    control.style.top = `calc(${rect.top}px - 48px)`;
    control.style.left = `calc(${rect.left}px + calc(${rect.width}px / 2) - 40px)`;
    control['text']= text; 
    document.body.appendChild(control);
  }
}

在此代码中,我们首先获取<template>内选择菜单控件的副本,然后将其分配给control变量。

接下来,我们为承载示例文本的元素的onpointerup事件编写处理程序函数。 在函数内部,我们使用document.getSelection()获取选择和选定的字符串。 如果选定的字符串为空,那么我们通过getBoundingClientRect()获取选中区域的大小和位置,并将它们放置在rect变量中。

使用rect,我们计算并分配controltopleft位置。 通过这种方式,选择菜单控件放置在选中区域的上方一点,并在水平方向上居中。 我们还将选定的字符串分配给control的用户定义属性。 这将在稍后用于共享文本。

最后,我们使用appendChild()control添加到网页中。 在这一点上,如果我们在页面上选择一些示例文本,选择菜单控件将出现在屏幕上。

现在我们开始编写当单击选择菜单控件时会发生什么。 换句话说,我们将使文本在单击提示时发布到 Twitter。

control.addEventListener('pointerdown', oncontroldown, true);

function oncontroldown(event) {
  window.open(`https://twitter.com/intent/tweet?text=${this.text}`)
  this.remove();
  document.getSelection().removeAllRanges();
  event.stopPropagation();
}

当单击控件时,会打开一个包含 Twitter 的“新推文”页面的选项卡,其中包含准备发布的选定文本。

在推文提示后,不再需要选择菜单控件,并且将其删除,以及页面上进行的任何选择。 pointerdown事件沿 DOM 树进一步级联的方式也在此时停止。

我们还需要页面onpointerdown事件的事件处理程序

document.onpointerdown = ()=> {    
  let control = document.querySelector('#control');
  if (control !== null) {control.remove();document.getSelection().removeAllRanges();}
}

现在,当单击页面上的任何位置(但选择菜单控件除外)时,控件和页面上进行的任何选择都将被删除。

演示

这是一个 Chris 整理的更漂亮的版本

这是一个显示选择菜单中多个控件的示例

关于 <template>

使用它并非完全必要。 相反,您还可以尝试以其他方式隐藏和显示控件,例如hiddenHTML 属性或 CSS display。 您也可以在 JavaScript 本身中构建选择菜单控件。 编码选择将取决于您执行它们的效率及其回退(如果需要),以及它们如何与您的应用程序相适应。

一些 UI/UX 建议

虽然这是一个不错的效果,但在使用它以确保良好的用户体验时,需要考虑一些事项。 例如,避免将您自己的文本注入文本选择中——您知道,例如在自动生成的推文中附加指向您网站的链接。 这是侵入性和烦人的。 如果有任何理由这样做,例如添加来源引用,请让用户在发布之前查看最终文本的预览。 否则,用户可能会对添加感到困惑或意外。

还有一件事:最好让菜单控件远离。 我们不希望它遮盖太多周围的内容。 这种事情加起来就是 CSS “数据丢失”,我们希望避免这种情况。

底线:了解您的用户为什么要在您的网站上选择文本,并以一种不会妨碍他们尝试执行的操作的方式添加控件。