html - Selection and deletion problems with Non-Editable Span inside ContentEditable DIV -


i have div contenteditable set true. inside there several spans contenteditable set false.

i trap backspace key if element under cursor <span> can delete it.

the problem works alternately odd spans only.

so, example, html code below, put cursor @ end of text in div, , press backspace way till beginning of div. observe select/delete first span, leave second, select/delete third span, leave fourth , on.

this behavior on internet explorer. works expected on firefox.

how should make behavior consistant in internet explorer?

following html code can used reproduce behavior:

var editablediv = document.getelementbyid('editablediv');    editablediv.onkeydown = function(event) {    var ignorekey;    var key = event.keycode || event.charcode;    if (!window.getselection) return;    var selection = window.getselection();    var focusnode = selection.focusnode,      anchornode = selection.anchornode;      if (key == 8) { //backspace      if (!selection.iscollapsed) {        if (focusnode.nodename == 'span' || anchornode.nodename == 'span') {          anchornode.parentnode.removechild(anchornode);          ignorekey = true;        }      } else if (anchornode.previoussibling && anchornode.previoussibling.nodename == 'span' && selection.anchoroffset <= 1) {        selecttext(event, anchornode.previoussibling);        ignorekey = true;      }    }    if (ignorekey) {      var evt = event || window.event;      if (evt.stoppropagation) evt.stoppropagation();      evt.preventdefault();      return false;    }  }    function selecttext(event, element) {    var range, selection;    editablediv.focus();    if (document.body.createtextrange && element.nodename == 'span') {      range = document.body.createtextrange();      range.movetoelementtext(element);      range.select();    } else if (window.getselection) {      selection = window.getselection();      range = document.createrange();      range.selectnodecontents(element);      selection.removeallranges();      selection.addrange(range);    }    var evt = (event) ? event : window.event;    if (evt.stoppropagation) evt.stoppropagation();    if (evt.cancelbubble != null) evt.cancelbubble = true;    return false;  }
#editablediv {    height: 75px;    width: 500px;    font-family: consolas;    font-size: 10pt;    font-weight: normal;    letter-spacing: 1px;    background-color: white;    overflow-y: scroll;    overflow-x: hidden;    border: 1px solid black;    padding: 5px;  }  #editablediv span {    color: brown;    font-family: verdana;    font-size: 8.5pt;    min-width: 10px;    _width: 10px;  }  #editablediv p,  #editablediv br {    display: inline;  }
<div id="editablediv" contenteditable="true">    &nbsp;(<span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>field1</span> < 500) <span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>or</span> (<span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>field2</span> > 100 <span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>and</span> (<span contenteditable='false' onclick='selecttext(event, this);'      unselectable='on'>field3</span> <=200) )   </div>

edit

just fyi. have asked question in msdn forum well.

the challenge ie11 backspace right directly against <span>.  next backspace select , highlight it.  seems such simple objective, ie11 won't cooperate.  there should quick easy patch, right?  , bugs begin.

the approach came walk tree backwards first previous non-empty node, clearing empty nodes between appease ie, , evaluate few conditions.  if caret should end @ right side of <span>, manually (because ie won't) creating new range obj selection there @ end of <span>.

online demo


i added additional kludge ie in case 2 spans dragged against eachother.  example, field2field3.  when backspace right onto field3, backspace once again delete it, ie jump caret leftward on field2.  skip right on field2.  grrr.  workaround intercept , insert space between pair of spans.  wasn't confident you'd happy that.  but, know, it's workaround.  anyway, turned-up yet bug, ie changes inserted space 2 empty textnodes.  more grrr.  , workaround workaround.  see non-iscollapsed code. 

code snippet

var editablediv = document.getelementbyid('editablediv');           editablediv.onkeydown = function(event) {           var ignorekey;           var key = event.keycode || event.charcode;           if (!window.getselection) return;           var selection = window.getselection();           var focusnode = selection.focusnode,             anchornode = selection.anchornode;             var anchoroffset = selection.anchoroffset;             if (!anchornode) return             if (anchornode.nodename.tolowercase() != '#text') {             if (anchoroffset < anchornode.childnodes.length)               anchornode = anchornode.childnodes[anchoroffset]             else {               while (!anchornode.nextsibling) anchornode = anchornode.parentnode // might step out of editablediv "justincase" comment node               anchornode = anchornode.nextsibling             }             anchoroffset = 0           }             function backseek() {               while ((anchoroffset == 0) && (anchornode != editablediv)) {                 if (anchornode.previoussibling) {                 if (anchornode.previoussibling.nodename.tolowercase() == '#text') {                   if (anchornode.previoussibling.nodevalue.length == 0)                     anchornode.parentnode.removechild(anchornode.previoussibling)                   else {                     anchornode = anchornode.previoussibling                     anchoroffset = anchornode.nodevalue.length                   }                 } else if ((anchornode.previoussibling.offsetwidth == 0) && (anchornode.previoussibling.offsetheight == 0))                   anchornode.parentnode.removechild(anchornode.previoussibling)                   else {                   anchornode = anchornode.previoussibling                     while ((anchornode.lastchild) && (anchornode.nodename.touppercase() != 'span')) {                       if ((anchornode.lastchild.offsetwidth == 0) && (anchornode.lastchild.offsetheight == 0))                       anchornode.removechild(anchornode.lastchild)                       else if (anchornode.lastchild.nodename.tolowercase() != '#text')                       anchornode = anchornode.lastchild                       else if (anchornode.lastchild.nodevalue.length == 0)                       anchornode.removechild(anchornode.lastchild)                       else {                       anchornode = anchornode.lastchild                       anchoroffset = anchornode.nodevalue.length                         //break							//don't need break, textnode has no children                     }                   }                   break                 }               } else                 while (((anchornode = anchornode.parentnode) != editablediv) && !anchornode.previoussibling) {}             }           }             if (key == 8) { //backspace             if (!selection.iscollapsed) {                 try {                 document.createelement("select").size = -1               } catch (e) { //kludge ie when 2+ spans back-to-back adjacent                   if (anchornode.nodename.touppercase() == 'span') {                   backseek()                   if (anchornode.nodename.touppercase() == 'span') {                     var k = document.createtextnode(" ") // doesn't work here between 2 spans.  ie makes 2 empty textnodes instead !                     anchornode.parentnode.insertbefore(k, anchornode) // works                     anchornode.parentnode.insertbefore(anchornode, k) // simulate "insertafter"                   }                 }               }                 } else {               backseek()                 if (anchornode == editablediv)                 ignorekey = true                 else if (anchornode.nodename.touppercase() == 'span') {                 selecttext(event, anchornode)                 ignorekey = true               } else if ((anchornode.nodename.tolowercase() == '#text') && (anchoroffset <= 1)) {                   var prev, anchornodesave = anchornode,                   anchoroffsetsave = anchoroffset                 anchoroffset = 0                 backseek()                 if (anchornode.nodename.touppercase() == 'span') prev = anchornode                 anchornode = anchornodesave                 anchoroffset = anchoroffsetsave                   if (prev) {                   if (anchoroffset == 0)                     selectevent(prev)                     else {                     var r = document.createrange()                     selection.removeallranges()                       if (anchornode.nodevalue.length > 1) {                       r.setstart(anchornode, 0)                       selection.addrange(r)                       anchornode.deletedata(0, 1)                      }                      else {                       (var = 0, p = prev.parentnode; true; i++)                         if (p.childnodes[i] == prev) break                       r.setstart(p, ++i)                       selection.addrange(r)                       anchornode.parentnode.removechild(anchornode)                     }                   }                   ignorekey = true                 }               }             }           }           if (ignorekey) {             var evt = event || window.event;             if (evt.stoppropagation) evt.stoppropagation();             evt.preventdefault();             return false;           }         }           function selecttext(event, element) {           var range, selection;           editablediv.focus();           if (window.getselection) {             selection = window.getselection();             range = document.createrange();             range.selectnode(element)             selection.removeallranges();             selection.addrange(range);           } else {             range = document.body.createtextrange();             range.movetoelementtext(element);             range.select();           }           var evt = (event) ? event : window.event;           if (evt.stoppropagation) evt.stoppropagation();           if (evt.cancelbubble != null) evt.cancelbubble = true;           return false;         }
#editablediv {            height: 75px;            width: 500px;            font-family: consolas;            font-size: 10pt;            font-weight: normal;            letter-spacing: 1px;            background-color: white;            overflow-y: scroll;            overflow-x: hidden;            border: 1px solid black;            padding: 5px;          }          #editablediv span {            color: brown;            font-family: verdana;            font-size: 8.5pt;            min-width: 10px;            /*_width: 10px;*/            /* this? */          }          #editablediv p,          #editablediv br {            display: inline;          }
<div id="editablediv" contenteditable="true">  &nbsp;(<span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>field1</span> < 500)  <span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>or</span> (<span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>field2</span> > 100 <span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>and</span> (<span contenteditable='false' onclick='selecttext(event, this);' unselectable='on'>field3</span> <= 200) )  </div>


Comments

Popular posts from this blog

searchKeyword not working in AngularJS filter -

sequelize.js - Sequelize: sort by enum cases -

user interface - how to replace an ongoing process of image capture from another process call over the same ImageLabel in python's GUI TKinter -