{"id":338189,"date":"2021-04-28T07:19:54","date_gmt":"2021-04-28T14:19:54","guid":{"rendered":"https:\/\/css-tricks.com\/?p=338189"},"modified":"2021-04-28T07:19:56","modified_gmt":"2021-04-28T14:19:56","slug":"how-to-create-actions-for-selected-text-with-the-selection-api","status":"publish","type":"post","link":"https:\/\/css-tricks.com\/how-to-create-actions-for-selected-text-with-the-selection-api\/","title":{"rendered":"How to Create Actions for Selected Text With the Selection API"},"content":{"rendered":"\n

Click, drag, release: you\u2019ve just selected some text on a webpage\u2009\u2014\u2009probably to copy and paste it somewhere or to share it. Wouldn’t it be cool if selecting that text revealed some options that make those tasks easier? That\u2019s what a selection menu does.<\/p>\n\n\n\n

You may already be familiar with selection menus if you\u2019ve ever used an online editor. When you select text, options to format the selection might float above it. In fact, I\u2019m writing this draft in an editor that does exactly this.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

Let\u2019s see how we can create a selection menu like this using JavaScript\u2019s Selection API<\/a>. The API gives us access to the space and content of the selected area on a webpage. This way we can place the selection menu exactly above<\/strong> the selected text and get access to the selected text itself.<\/p>\n\n\n\n\n\n\n\n

Here\u2019s an HTML snippet with some sample text:<\/p>\n\n\n\n

<article>\n  <h1>Select over the text below<\/h1> \n  <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>\n<\/article>\n<template><span id=\"control\"><\/span><\/template><\/code><\/pre>\n\n\n\n

There\u2019s a <template><\/code> tag at the end there. The <span><\/code> inside it is our selection menu control. Anything inside a <template><\/code> tag is not rendered<\/strong> on the page until it\u2019s later added to the page with JavaScript. We\u2019ll add the selection menu control to the page when user selects text. And when the user selects that text, our selection menu will prompt the user to tweet it.<\/p>\n\n\n\n

\"\"<\/figure>\n\n\n\n

Here\u2019s the CSS to style it:<\/p>\n\n\n\n

#control {\n    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>\");\n  cursor: pointer;\n  position: absolute;\n  width: 40px;\n  height: 40px;\n}\n#control::before{\n  background-color: black;\n  color: white;\n  content: \" tweet this! \";\n  display: block;\n  font-weight: bold;\n  margin-left: 37px;\n  margin-top: 6px;\n  padding: 2px;\n  width: max-content;\n  height: 20px;\n}<\/code><\/pre>\n\n\n\n

Check out this article<\/a> to learn how I used an emoji (💬) for the background image.<\/p>\n\n\n\n

So far, the sample text is ready, and the selection menu control has been styled. Let\u2019s move on to the JavaScript. When a selection is made, we\u2019ll get the size and position of the selected area on the page. We then use those measurements to assign the position of the selection menu control\u2009at the top-middle of the selected area.<\/p>\n\n\n\n

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

In this code, we first get a copy of the selection menu control inside <template><\/code>, then assign it to the control<\/code> variable.<\/p>\n\n\n\n

Next, we write the handler function for the onpointerup<\/code> event of the element carrying the sample text. Inside the function, we get the selection and the selected string using document.getSelection()<\/code>. If the selected string is not<\/strong> empty, then we get the selected area\u2019s size and position, via getBoundingClientRect()<\/code>, and place it in the rect<\/code> variable.<\/p>\n\n\n\n

Using rect<\/code>, we calculate and assign the top<\/code> and left<\/code> positions of the control<\/code>. This way, the selection menu control is placed a little above the selected area and centered horizontally. We\u2019re also assigning the selected string to a user-defined property of control<\/code>. This will later be used to share the text.<\/p>\n\n\n\n

And, finally, we add control<\/code> to the webpage using appendChild()<\/code>. At this point, if we select some of the sample text on the page, the selection menu control will appear on the screen.<\/p>\n\n\n\n

Now we get to code what happens when the selection menu control is clicked. In other words, we\u2019re going to make it so that the text is tweeted when the prompt is clicked.<\/p>\n\n\n\n

control.addEventListener('pointerdown', oncontroldown, true);\n\nfunction oncontroldown(event) {\n  window.open(`https:\/\/twitter.com\/intent\/tweet?text=${this.text}`)\n  this.remove();\n  document.getSelection().removeAllRanges();\n  event.stopPropagation();\n}<\/code><\/pre>\n\n\n\n

When the control is clicked, a tab opens with Twitter\u2019s \u201cNew Tweet\u201d page, complete with the selected text ready to go.<\/p>\n\n\n\n