0

I am attempting to move span tags and their contents to the left or right by one position when the cursor is inside of them and Ctrl Right or Control Left is pressed. The span tags are inside of a contenteditable paragraph. I am not even having any luck getting a test message to log to the console to indicate that the cursor is even inside of the span tag. Here is the fiddle:

https://jsfiddle.net/scooke/5tp5oe7z/

Javascript/Jquery

$(document).on('keyup','.move',function(e){ 
  if (e.ctrlKey && (e.which === 37 || e.which === 39)){
    //move character at right or left of span to the other side
    //to simulate the whole span moved  
  }    
  e.stopPropagation();   
 });

Sample Html

<p class="parent" contenteditable="true">Bacon ipsum dolor amet jowl chicken    pork loin <span class="move">[move text]</span>tail. Short ribs meatball
<br>bresaola beef boudin hamburger, cow rump swine. Pork belly ribeye leberkas venison
<br>ground <span class="move">[move text]</span>round</p>
Shawn Cooke
  • 907
  • 1
  • 11
  • 28
  • 4
    Instead of trying to fool the system and get around the rule of posting your code in your question when linking to jsFiddle, why not just do as you're asked to? If jsFiddle ever goes away or is inaccessible, your question loses all value to future visitors without your code in it – j08691 Nov 23 '15 at 19:36
  • This might help you: http://stackoverflow.com/questions/18771651/detect-keyup-event-of-contenteditable-child-whose-parent-is-a-contenteditable-di – technophobia Nov 23 '15 at 19:40
  • 2
    I went ahead and added the code. I did not know it made a difference and didn't mean to try to circumvent any rules. – Shawn Cooke Nov 23 '15 at 19:41
  • @technophobia I saw that post but there were some suggestions in some of the answers that this was a bug. Additionally, that was 2 years ago. Given that it might be a bug, I was looking for a more succinct way to do this. – Shawn Cooke Nov 23 '15 at 19:44

1 Answers1

2
  1. Your code fails because (unfortunately) span elements do not fire keydown/keyup/keypress events - no, not even when they are part of a contenteditable.

    Therefore, you need to catch the event on the contenteditable element itself.

  2. Handling TextNodes in jQuery is as complicated (or more complicated) as doing it in native JS

    Relevant properties & methods of the standard Node class:

    • Node.prototype.data - content of the (text) node
    • Node.prototype.length - length of the content
    • Text.prototype.insertContent(position, content) - insert text
    • Text.prototype.appendConent(content) - append text
    • Text.prototype.deleteContent(position, length) - delete part of the content

That being said, this should provide the Ctrl+left/right-moving you expect: (JSFiddle)

$('p[contenteditable]').on('keydown', function (e) { 
    // Only handle event if ctrl+left / ctrl+right
    if (!e.ctrlKey || (e.which != 37 && e.which != 39)) return;
    // and selection is in a span.move
    var sel = document.getSelection();
    var node = sel.anchorNode;
    if (!node || node != sel.focusNode) return;
    // Text "[move text]" is in selection, get <span> parent
    if (node.nodeType == Node.TEXT_NODE) {
        node = node.parentNode;
    }
    if (!node || node.nodeName != 'SPAN' || node.className != 'move') return;

    // Do the magic
    moveSpan(node, e.which == 37); // 37: left, 39: right
    e.preventDefault();
    e.stopPropagation();
});

function moveSpan (span, toLeft) {
    var left = span.previousSibling;
    var right = span.nextSibling;

    if (!left || left.nodeType != Node.TEXT_NODE || !right || right.nodeType != Node.TEXT_NODE) return;
    if (toLeft && !left.length) return;
    if (!toLeft && !right.length) return;

    if (toLeft) {
        right.insertData(0, left.data[left.length - 1]);
        left.deleteData(left.length - 1, 1);
    } else {
        left.appendData(right.data[0]);
        right.deleteData(0, 1);
    }
}

Note: Only works on IE9+, the selection API on IE8- is non-standard.

Leon Adler
  • 2,993
  • 1
  • 29
  • 42
  • Probably one of the best explanations I have ever heard. And, one that would solve about 3 other SO postings attempting to do the same thing, that I could not glean anything from. Thanks so much for taking the time to do this. – Shawn Cooke Nov 24 '15 at 00:26
  • Thanks for the appreciation, motivates to take the time again ;-) – Leon Adler Nov 24 '15 at 04:44
  • Is there any way to make this work for _l_ and _r_ keys in addition to the ctrl arrow keys? This way mobile users could use it. FYI: there will never be an _l_ or an _r_ inside the brackets since the brackets contain guitar chords so no modifier key is needed. Thanks again Leon – Shawn Cooke Dec 02 '15 at 12:36
  • @TripleC Sure, instead only 37 & 39, test also for 76 & 82. See https://jsfiddle.net/kcw24owb/3/ – Leon Adler Dec 02 '15 at 19:04