2014-07-19 9 views
7

Próbuję zastąpić każdą instancję /any thing in here/ z <b>/any thing in here/</b> w locie, ponieważ zmiany są wprowadzane w contenteditable div.Modyfikuj tekst w contenteditable div bez ponownego ustawiania pozycji kursora (kursora)

Moja obecna implementacja działa, ale przy każdym naciśnięciu klawisza karetka jest przenoszona na początek elementu div, co uniemożliwia wykorzystanie implementacji. Czy jest jakiś sposób na zachowanie pozycji karetki podczas zastępowania zawartości div?

$('.writer').on('keyup', function(e) { 
    $(this).html($(this).html().replace(/\/(.*)\//g, '<b>\/$1\/<\/b>')); 
}); 
+0

Czy istnieje powód, dla którego trzeba użyć 'div' z' contenteditable' zamiast 'textarea' lub coś podobnego? – royhowie

+0

@Luxelin: Chciałbym renderować html w div, i o ile wiem, HTML nie może być renderowany wewnątrz textarea. – slyv

+0

Nawet jeśli używałbyś textarea, jestem pewien, że karetka nadal się poruszałaby. –

Odpowiedz

4

spróbować demo

$('#writer').on('keyup', function(e) { 
     var range = window.getSelection().getRangeAt(0); 
     var end_node = range.endContainer; 
     var end = range.endOffset; 
     if(end_node != this){ 
      var text_nodes = get_text_nodes_in(this); 
      for (var i = 0; i < text_nodes.length; ++i) { 
       if(text_nodes[i] == end_node){ 
        break; 
       } 
       end += text_nodes[i].length; 
      } 
     } 
     var html = $(this).html(); 
     if(/\&nbsp;$/.test(html) && $(this).text().length == end){ 
      end = end - 1; 
      set_range(end,end,this); 
      return; 
     } 
     var filter = html.replace(/(<b>)?\/([^<\/]*)(<\/b>)?/g, '\/$2'); 
     console.log(filter); 
     filter = filter.replace(/(<b>)?([^<\/]*)\/(<\/b>)?/g, '$2\/'); 
     console.log(filter); 
     filter = filter.replace(/(<b>)?\/([^<\/]*)\/(<\/b>)?/g, '<b>\/$2\/<\/b>'); 
     console.log(filter); 
     if(!/\&nbsp;$/.test($(this).html())){ 
      filter += '&nbsp;'; 
     } 
     $(this).html(filter); 
     set_range(end,end,this); 

    }); 

    $('#writer').on('mouseup', function(e) { 
     if(!/\&nbsp;$/.test($(this).html())){ 
      return; 
     } 
     var range = window.getSelection().getRangeAt(0); 
     var end = range.endOffset; 
     var end_node = range.endContainer; 
     if(end_node != this){ 
      var text_nodes = get_text_nodes_in(this); 
      for (var i = 0; i < text_nodes.length; ++i) { 
       if(text_nodes[i] == end_node){ 
        break; 
       } 
       end += text_nodes[i].length; 
      } 
     } 
     if($(this).text().length == end){ 
      end = end - 1; 
      set_range(end,end,this); 
     } 
    }); 

    function get_text_nodes_in(node) { 
     var text_nodes = []; 
     if (node.nodeType === 3) { 
      text_nodes.push(node); 
     } else { 
      var children = node.childNodes; 
      for (var i = 0, len = children.length; i < len; ++i) { 
       var text_node 
       text_nodes.push.apply(text_nodes, get_text_nodes_in(children[i])); 
      } 
     } 
     return text_nodes; 
    } 

    function set_range(start, end, element) { 
     var range = document.createRange(); 
     range.selectNodeContents(element); 
     var text_nodes = get_text_nodes_in(element); 
     var foundStart = false; 
     var char_count = 0, end_char_count; 

     for (var i = 0, text_node; text_node = text_nodes[i++];) { 
      end_char_count = char_count + text_node.length; 
      if (!foundStart && start >= char_count && (start < end_char_count || (start === end_char_count && i < text_nodes.length))) { 
       range.setStart(text_node, start - char_count); 
       foundStart = true; 
      } 
      if (foundStart && end <= end_char_count) { 
       range.setEnd(text_node, end - char_count); 
       break; 
      } 
      char_count = end_char_count; 
     } 

     var selection = window.getSelection(); 
     selection.removeAllRanges(); 
     selection.addRange(range); 
    }