1

Grammarly is somehow able to put its button on the bottom right of text boxes, as shown in the gif. Moreover, the button moves when the textbox moves.

I'm building a chrome extension that puts a similar button on text boxes (bottom-right). I can get hold of the textbox element by listening to focusin event, so this question is about placing a button on that. So far, I'm not able to find the right approach to do that. I'd also prefer to move my button on the left of any existing buttons like Grammarly to avoid overlapping.

enter image description here

GorvGoyl
  • 42,508
  • 29
  • 229
  • 225
  • 1
    It's placed there using css – Konrad Dec 01 '22 at 20:19
  • Notice that in your stackoverflow example, the button is placed under the div that's directly parenting the textarea. The div has the same dimensions as the textarea and is at the same place, so simply appending the element and setting styles like `position: absolute; bottom: 8px; right: 8px;` does it. – code Dec 01 '22 at 21:13

3 Answers3

1

On StackOverflow, the textarea is wrapped directly by a div of the same size. The button was placed as a child to the div element, then possibly positioned with simple CSS, e.g. position: absolute; right: 8px; bottom: 8px;.

Obviously however, that's not how all textareas work, so my best solution (which is far from optimal) is to calculate the position of the textarea and place it accordingly. See the example:

const OFFSET = 5;

const button = document.createElement("button");

[...document.getElementsByTagName("textarea")].forEach(textarea => {
  // get the dimensions of each element, then position the buttons accordingly
  const { top, left, height, width } = textarea.getBoundingClientRect();
  const b = button.cloneNode(true);
  document.body.appendChild(b); // it's necessary to first append the element so CSS wil be applied, otherwise the dimensions will be 0
  
  const { width: bWidth, height: bHeight } = b.getBoundingClientRect();
  b.style.top = (
    (
      top + window.scrollY + height
    ) - OFFSET - bHeight
  ) + "px";
  b.style.left = (
    (
      left + window.scrollX + width
    ) - OFFSET - bWidth
  ) + "px";
  
  // if you want to do stuff with the button relative to that text area
  b.addEventListener("click", () => doSomething(textarea));
});

function doSomething(textarea) {
  console.log(textarea.value);
}
/* just for the demo */
textarea {
  display: block;
}

button {
  color: white;
  background-color: lightgreen;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  box-shadow: 1px 1px 2px 0px rgba(0, 0, 0, 0.3);
  
  /* necessary button CSS: */
  position: absolute;
}

html {
  position: relative;
}
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
<textarea>text...</textarea>
code
  • 5,690
  • 4
  • 17
  • 39
  • hey, so this worked. thanks. Do you also happen to know the workaround to my 2nd part of the question: how can I move my button on the left of any existing buttons like Grammarly to avoid overlapping? – GorvGoyl Dec 03 '22 at 11:27
  • 1
    @GorvGoyl well... there may be edge cases, but what you can do is take the x and y coordinates where you intended to append your button, feed it to [`document.elementFromPoint`](https://developer.mozilla.org/en-US/docs/Web/API/DocumentOrShadowRoot/elementFromPoint), and check if what's returned was the textarea you were targeting. If not, get the size of that element and move your button over a bit. Hope that helps? – code Dec 03 '22 at 18:34
0

Since textarea element can't have a child like a button, you need to make a parent around the textarea which then could hold a button and a textarea. Then you want to make the botton absolute. It would work like the following example:

html:

 <div class="wrapper">
  <textarea></textarea>
  <button></button>
</div>

css:

.wrapper {
    position: relative;
    width: max-content;
  }
  textarea {
    width: 15rem;
    height: 10rem;
  }
  button {
    position: absolute;
    right: 1rem;
    bottom: 1rem;
    width: 1rem;
    height: 1rem;
  }

the parents size is automatic the size of the child. So if you resize the textarea, the div will also be resize. Since the button is placed absolute at the bottom right corner of the div, it will alway move when you resize.

I've found a new and short option. If you do it like this it should work fine.

const textarea = document.querySelector("textarea");
const button = document.querySelector("button");
function setPosition() {
  const left = textarea.offsetLeft;
  const top = textarea.offsetTop;
  const width = textarea.offsetWidth;
  const height = textarea.offsetHeight;

  button.setAttribute(
    "style",
    "position: absolute; top:" +
      (top + height - 30) +
      "px; left: " +
      (left + width - 30) +
      "px;"
  );
}
setPosition();

new ResizeObserver(setPosition).observe(textarea);

Hope i could help!

  • interesting approach but changing the hierarchy of the textbox position in dom might brake styling in some sites – GorvGoyl Dec 01 '22 at 20:54
  • Another option would be to use Element.getBoundingClientRect(), but that could be a lot of calculations and make the site lag. – TrueStoryShort Dec 01 '22 at 21:06
  • you will need this to call everytime the textarea is resized. The following stackoverflow answers could help with this: https://stackoverflow.com/questions/5570390/resize-event-for-textarea – TrueStoryShort Dec 01 '22 at 21:22
  • the second method doesn't align button to bottom-right.. you can test on https://news.ycombinator.com/item?id=33842276 – GorvGoyl Dec 03 '22 at 11:11
0

Add a div with your button after the textarea, and use some CSS to position it:

#myScrollContainer {
  height: 150px;
  overflow: auto;
}
#myTextareaContainer {
  width: 300px;
  position: relative;
}
#myTextarea {
  width: 300px;
}
#myOverlay {
  position: absolute;
  right: 25px;
  bottom: 10px;
}
<div id="myScrollContainer">
<div id="myTextareaContainer">
<form>
<textarea rows="5" cols="50" id="myTextarea">
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</textarea>
</form>
<div id="myOverlay">
<button>Me!</button>
</div>
</div>
.<br />
(scroll down)<br />
.<br />
.<br />
.<br />
.<br />
.<br />
.<br />
.<br />
The end
</div>

Note that you might want to position the button dynamically based on the visibility of the scrollbar.

Peter Thoeny
  • 7,379
  • 1
  • 10
  • 20
  • not the downvoter but myTextareaContainer with position relative might not always be present on sites so the button positioning would be way off – GorvGoyl Dec 01 '22 at 21:00
  • @GorvGoyl: I don't get it. Did you try the code? – Peter Thoeny Dec 01 '22 at 21:03
  • it would work as long as there's a parent relative element of the textarea element but that might not be the case always. if you remove position relative from myTextareaContainer it'd break. – GorvGoyl Dec 01 '22 at 21:19