Alex McMillan
Alex McMillan

Reputation: 17952

Get caret position on click event

I'm having a hard time doing something that should be simple: getting the caret position where a user clicks in a Paragraph. This is complicated because I'm creating and destroying instances of the WYSIWYG text editor Redactor contextually based on user input, which modifies the DOM.

My structure:

<div class='node'>
    <div class='hierarchy'></div>
    <div class='content'>
        <h2>Test Heading</h2>
        <p>Content that user might click on.  Need to find position where I should insert cursor after initialising Redactor</p>
    </div>
</div>

When the user clicks on the text, I'm initialising an instance of redactor, which modifies the DOM like so:

<div class='node'>
    <div class="hierarchy"></div>
    <div class="redactor_box">
        <div id="redactorToolbar">... ul etc ...</div>
        <div class="content redactor_editor" contenteditable="true">
            <h2>Test Heading</h2>
            <p>Content that user might click on.  Need to find position where I should insert cursor after initialising Redactor</p>
        </div>
        <textarea dir="ltr" style="height: 90px; display: none;"></textarea>
    </div>
</div>

After the redactor has been initialised, I want to position the cursor where the user originally clicked. Pretty straightforward, right? Well, in my click handler (which is attached to the div.node), window.getSelection() returns the position of the caret PRIOR TO the click:

Node.prototype.onClick = function (e, ui) {
    var $el = $(this),
        node = $el.data('node'),
        document = node.document,
        selection = window.getSelection();

    console.log('rangeCount: ', selection.rangeCount);
    console.log('type: ', selection.type);
    console.log('anchorOffset: ', selection.anchorOffset);

    if ($el.has('.redactor_box').length <= 0) {
        document.setRedactor(node);
    }
};

Say for example I:

  1. Click on <p> at char 5
    • Prints "rangeCount: 0, type: None, anchorOffset: 0"
    • Initialises Redactor, wrapping the content DIV and making it contenteditable='true'
  2. Click inside the <p> at char position 108
    • Prints "rangeCount: 1, type: Caret, anchorOffset: 108" (EXPECTED)
  3. Click a different nodes <p> at char 5
    • Prints "rangeCount: 1, type: Caret, anchorOffset: 108" (NOT EXPECTED)
    • Destroys redactor and recreates around 2nd node

In steps 1 & 3, the "selection" clearly hasn't been passed to the <p>. I assume this is because at these points, the <p> in question doesn't have any contenteditable='true' in its ancestry so it can't really have a "selection" per se.

My Question:

How can I get the caret position under the mouse cursor within a non-contenteditable'd <p> tag when the user clicks one?

Any help/advice much appreciated!

(Unfortunately, Redactor is not freely available so I can't include it in a jsFiddle example)

UPDATE

These "node" divs are also using jQueryUI's draggable and droppable widgets. Upon disabling these, the handler outputs the expected values for the selection... interesting. Unfortunately the drag/drop functionality is a requirement..

Upvotes: 2

Views: 2443

Answers (1)

Niet the Dark Absol
Niet the Dark Absol

Reputation: 324790

I don't know if this'll work, but try adding a small setTimeout before getting the selection. This should cause it to give you the selection after the click, and in that callback you can then initialise the editor.

Upvotes: 0

Related Questions