Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javascript - Get element behind caret in child elements of [contenteditable]

I am building a simplistic and easy-to-use text editor in Javascript, basically a WYSIWYG editor. I will be using the contenteditable attribute to the main wrapper (.wrapper). When you press enter inside the .wrapper, a new <p> element with a unique id gets appended to the wrapper.

I need a way to fetch which child element of the .wrapper that is currently selected (i.e., being focused or having the caret/text marker inside of it).

I've searched for days without any results, and I've tried using document.elementFromPoint() but without any proper results.

When using $(":focus"), I get the entire .wrapper and not the specific child element.

Edit:

Example HTML structure:

<div class="container t-wrapper" contenteditable>
</div>

Example Javascript code:

$(document).ready(function() {
    $currentElement = $("[contenteditable]");

    $(".t-wrapper").each(function() {
        var id = generateIdentifier(6); // Ignore this

        /* This creates the initial <p> child element, where you start typing. */
        $(this).html('<p class="t-empty t-first" id="' + id + '"></p>');

        $(this).on("mouseup", function() {
            /* $currentElement = whatever element the caret is inside. */
        });

        $(this).on("keyup", function() {
            /* $currentElement = whatever element the caret is inside. */
        });
    ));
});

Edit 2:

I managed to get it fairly working with the mouseup event, since you're actually clicking on something. But I need this to work when moving the caret using the keyboard. Alternatively, I need some way to get the position of the caret in pixels, and then use document.elementFromPoint() to get the specific element.

like image 729
Daniel Hallgren Avatar asked Nov 08 '22 19:11

Daniel Hallgren


1 Answers

:focus doesn't select your elements because they are not focusable.

You can make them focusable by adding tabindex="-1" in HTML, or tabIndex = -1 in JS.

var generateIdentifier = Math.random;
var currentElement = document.querySelector("[contenteditable]");
[].forEach.call(document.querySelectorAll(".t-wrapper"), function(el) {
  var first = document.createElement('p');
  first.className = "t-empty t-first";
  first.id = generateIdentifier(6);
  first.textContent = 'Press enter to create new paragraphs';
  first.tabIndex = -1;
  el.appendChild(first);
});
.container > :focus {
  border: 1px solid blue;
}
<div class="container t-wrapper" contenteditable></div>

It seems that if you add it to the first paragraph, new paragraphs obtain it automatically. But if you want to be sure, I guess you could use a mutation observer or a keyup event listener to detect paragraphs without tabindex, and add it:

el.addEventListener("keyup", function(e) {
  var newChild = el.querySelector('p:not([tabindex])');
  if(newChild) newChild.tabIndex = -1;
});
like image 171
Oriol Avatar answered Nov 14 '22 21:11

Oriol