-1

I'm developing a webapp, and I have a problem with the JavaScript.

The following is a simplified version of the HTML causing the problem. I have some nested contenteditable divs (I replaced the real content with placeholder text):

<div contenteditable="true" id="div1">
    text
    <div contenteditable="inherit" id="div2">
        text
        <div contenteditable="inherit" id="div3">
            text
        </div>
        text
    </div>
    text
</div>

I want to get the element that's selected (being edited by the user) via JavaScript, but so far I haven't found a way to do it (successfully).


What I have tried and why it doesn't work:

I have tried using document.activeElement, which is supposed to return whichever element is in focus. Normally this works, but it doesn't produce the desired result when working with nested contenteditable elements. Instead of returning the element that's being edited by the user, it returns the uppermost contenteditable ancestor.

For instance, if div2 is selected/being edited, document.activeElement returns div1. If div3 was selected/being edited, document.activeElement also returns div1.

So I guess document.activeElement is not the right way to go about this.


How do I get the most specific element that's being edited, not its uppermost contenteditable ancestor?

clickbait
  • 2,818
  • 1
  • 25
  • 61

4 Answers4

12

I did it by inserting a dummy element at the caret position and finding it's direct parent.

function getActiveDiv() {
    var sel = window.getSelection();
    var range = sel.getRangeAt(0);
    var node = document.createElement('span');
    range.insertNode(node);
    range = range.cloneRange();
    range.selectNodeContents(node);
    range.collapse(false);
    sel.removeAllRanges();
    sel.addRange(range);
    var activeDiv = node.parentNode;
    node.parentNode.removeChild(node);
    return activeDiv;
}

I did an example on fiddle: https://jsfiddle.net/shhe05cj/4/

I run it on every keypress event that's bind to the relevant divs.

I based it on another thread that did something similar:Set caret position right after the inserted element in a contentEditable div

Community
  • 1
  • 1
fistuks
  • 469
  • 3
  • 5
3

Seems to work fine for me in this fiddle: http://jsfiddle.net/dgrundel/huL4sjem/

I'm using this code to check:

<div contenteditable="true" class="edit">
    This is editable.
</div>

<script>
    $('.edit').on('click', function(){
        console.log(document.activeElement);
    });
</script>

When I click into the editable element, console gets a copy of the div logged to it.

dgrundel
  • 568
  • 3
  • 7
  • Does it have to be like that for it to work? My div is contenteditable inherit, and my parent div is contenteditable. – clickbait Jun 27 '15 at 20:56
  • 2
    Yes. You are editing the entire `contenteditable` parent element, not just the child, so it will be what gets the focus. – dgrundel Jun 28 '15 at 12:41
  • 3
    Then the answer does not solve my problem. How do I get the most specific element in focus/being edited? – clickbait Sep 03 '16 at 16:45
2

$('#edit').on('click', function() {
  // this is the innermost *node*
  var an = window.getSelection().anchorNode;
  // this is the innermost *element*
  var ae = an;
  while (!( ae instanceof Element ))
    ae = ae.parentElement;
  $('#an').text(an.nodeValue);
  $('#ae').text(ae.outerHTML);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div contenteditable="true" id="edit">
  This is editable. <span>What?</span> <em>How about this?</em>
</div>
<br/>
Active Node:<br/>
<pre id="an">Nothing</pre>
<br/>
Active Element:<br/>
<pre id="ae">Nothing</pre>
<br/>

Here you go. Hit "Run snippet." Have fun.

The example below shows the behavior using the input event. This would mimic the behavior of a mutation observer.

$('#edit').on('input', function() {
  // this is the innermost *node*
  var an = window.getSelection().anchorNode;
  // this is the innermost *element*
  var ae = an;
  while (!( ae instanceof Element ))
    ae = ae.parentElement;
  $('#an').text(an.nodeValue);
  $('#ae').text(ae.outerHTML);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div contenteditable="true" id="edit">
  This is editable. <span>What?</span> <em>How about this?</em>
</div>
<br/>
Active Node:<br/>
<pre id="an">Nothing</pre>
<br/>
Active Element:<br/>
<pre id="ae">Nothing</pre>
<br/>

The example below shows the behavior using the keydown and keyup events. This would catch non-modifying keypresses like arrows and modifier keys.

$('#edit').on('keydown keyup', function() {
  // this is the innermost *node*
  var an = window.getSelection().anchorNode;
  // this is the innermost *element*
  var ae = an;
  while (!( ae instanceof Element ))
    ae = ae.parentElement;
  $('#an').text(an.nodeValue);
  $('#ae').text(ae.outerHTML);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div contenteditable="true" id="edit">
  This is editable. <span>What?</span> <em>How about this?</em>
</div>
<br/>
Active Node:<br/>
<pre id="an">Nothing</pre>
<br/>
Active Element:<br/>
<pre id="ae">Nothing</pre>
<br/>
TylerY86
  • 3,737
  • 16
  • 29
  • 1
    @Dimava Added examples to show the difference between the `input` and `keydown` and `keyup` ... I skipped `keypress` because that would be redundant, but you get the idea. :) – TylerY86 Sep 30 '16 at 01:28
  • 1
    @narawagames You marked this as an answer, then unmarked it? Why? – TylerY86 Sep 30 '16 at 03:22
0

This does the job for me:

   $(document).click(function(event) {
            console.log(event.target);
        });
Petko Kostov
  • 367
  • 1
  • 9