2013-04-24 14 views
7

Sto provando a passare tra i paragrafi contenteditable utilizzando i tasti di direzione. Non riesco a inserire un div contenente tutti i paragrafi in quanto potrebbe essere diviso da altri elementi non modificabili.Attraversare paragrafi contenteditable con i tasti freccia

Devo essere in grado di determinare la lunghezza del carattere della prima riga in modo che quando si preme il tasto freccia su quando il cursore si trova sulla linea, salirà al paragrafo precedente - si spera che mantenga la posizione del cursore relativa alla linea.

posso ottenere l'indice di cursore con:

function cursorIndex() { 
    return window.getSelection().getRangeAt(0).startOffset; 
} 

e impostarlo con: come si trova qui - Javascript Contenteditable - set Cursor/Caret to index

var setSelectionRange = function(element, start, end) { 
    var rng = document.createRange(), 
     sel = getSelection(), 
     n, o = 0, 
     tw = document.createTreeWalker(element, NodeFilter.SHOW_TEXT, null, null); 
    while (n = tw.nextNode()) { 
     o += n.nodeValue.length; 
     if (o > start) { 
      rng.setStart(n, n.nodeValue.length + start - o); 
      start = Infinity; 
     } 
     if (o >= end) { 
      rng.setEnd(n, n.nodeValue.length + end - o); 
      break; 
     } 
    } 
    sel.removeAllRanges(); 
    sel.addRange(rng); 
}; 

var setCaret = function(element, index) { 
    setSelectionRange(element, index, index); 
}; 

Pronunciare il cursore si trova nella riga superiore del terzo comma e la viene premuta la freccia verso l'alto, vorrei passare alla riga inferiore del secondo paragrafo

http://jsfiddle.net/Pd52U/2/

+1

Cosa stai cercando di fare che il normale funzionamento dei tasti freccia non funzioni? – kennebec

+0

Dire che il cursore si trova nella riga superiore del terzo paragrafo e che la freccia su è premuta, vorrei che salisse nella rom in basso del secondo paragrafo. –

+2

Ho implementato qualcosa di simile prima. Non c'è modo di farlo in tutti i browser se non misurando noiosamente le posizioni e le dimensioni dei personaggi. –

risposta

3

Sembra che non ci sia un modo semplice per farlo, ho il seguente esempio di lavoro. C'è un po 'di elaborazione quindi è un po' lento e può essere fuori dal personaggio dispari quando si muove su e giù tra i paragrafi.

Si prega di informarmi di eventuali miglioramenti che possono essere apportati.

http://jsfiddle.net/zQUhV/47/


Quello che ho fatto è dividere il paragrafo per ogni opera, inserirli in un nuovo elemento a uno a uno, controllando per un cambiamento di altezza - quando non cambia una nuova linea è stato aggiunto .

Questa funzione restituisce un array di oggetti riga che contiene il testo della linea, a partire indice e indice di fine:

(function($) { 
$.fn.lines = function(){ 
    words = this.text().split(" "); //split text into each word 
    lines = []; 

    hiddenElement = this.clone(); //copies font settings and width 
    hiddenElement.empty();//clear text 
    hiddenElement.css("visibility", "hidden"); 

    jQuery('body').append(hiddenElement); // height doesn't exist until inserted into document 

    hiddenElement.text('i'); //add character to get height 
    height = hiddenElement.height(); 
    hiddenElement.empty(); 

    startIndex = -1; // quick fix for now - offset by one to get the line indexes working 
    jQuery.each(words, function() { 
     lineText = hiddenElement.text(); // get text before new word appended 
     hiddenElement.text(lineText + " " + this); 
     if(hiddenElement.height() > height) { // if new line 
      lines.push({text: lineText, startIndex: startIndex, endIndex: (lineText.length + startIndex)}); // push lineText not hiddenElement.text() other wise each line will have 1 word too many 
      startIndex = startIndex + lineText.length +1; 
      hiddenElement.text(this); //first word of the next line 
     } 
    }); 
    lines.push({text: hiddenElement.text(), startIndex: startIndex, endIndex: (hiddenElement.text().length + startIndex)}); // push last line 
    hiddenElement.remove(); 
    lines[0].startIndex = 0; //quick fix for now - adjust first line index 
    return lines; 
} 
})(jQuery); 

Ora è possibile utilizzare che per misurare il numero di carattere fino al punto della cursore e applicare questo quando si attraversa il paragrafo per mantenere la posizione del cursore rispetto all'inizio della linea. Tuttavia, ciò può produrre risultati estremamente imprecisi se si considera la larghezza di una "i" per la larghezza di una "m".

Invece sarebbe meglio per trovare la larghezza della linea fino al punto del cursore:

function distanceToCaret(textElement,caretIndex){ 

    line = findLineViaCaret(textElement,caretIndex); 
    if(line.startIndex == 0) { 
    // +1 needed for substring to be correct but only first line? 
     relativeIndex = caretIndex - line.startIndex +1; 
    } else { 
     relativeIndex = caretIndex - line.startIndex; 
    } 
    textToCaret = line.text.substring(0, relativeIndex); 

    hiddenElement = textElement.clone(); //copies font settings and width 
    hiddenElement.empty();//clear text 
    hiddenElement.css("visibility", "hidden"); 
    hiddenElement.css("width", "auto"); //so width can be measured 
    hiddenElement.css("display", "inline-block"); //so width can be measured 

    jQuery('body').append(hiddenElement); // doesn't exist until inserted into document 

    hiddenElement.text(textToCaret); //add to get width 
    width = hiddenElement.width(); 
    hiddenElement.remove(); 

    return width; 
} 
function findLineViaCaret(textElement,caretIndex){ 
    jQuery.each(textElement.lines(), function() { 
     if(this.startIndex <= caretIndex && this.endIndex >= caretIndex) { 
      r = this; 
      return false; // exits loop 
     } 
    }); 
    return r; 
} 

Poi dividere la linea di tiro fino in personaggi e trovare il punto che più vicino corrisponde alla larghezza di cui sopra da parte aggiungendo i caratteri uno per uno fino a raggiungere il punto:

function getCaretViaWidth(textElement, lineNo, width) { 
    line = textElement.lines()[lineNo-1]; 

    lineCharacters = line.text.replace(/^\s+|\s+$/g, '').split(""); 

    hiddenElement = textElement.clone(); //copies font settings and width 
    hiddenElement.empty();//clear text 
    hiddenElement.css("visibility", "hidden"); 
    hiddenElement.css("width", "auto"); //so width can be measured 
    hiddenElement.css("display", "inline-block"); //so width can be measured 

    jQuery('body').append(hiddenElement); // doesn't exist until inserted into document 

    if(width == 0) { //if width is 0 index is at start 
     caretIndex = line.startIndex; 
    } else {// else loop through each character until width is reached 
     hiddenElement.empty(); 
     jQuery.each(lineCharacters, function() { 
      text = hiddenElement.text(); 
      prevWidth = hiddenElement.width(); 
      hiddenElement.text(text + this); 
      elWidth = hiddenElement.width(); 
      caretIndex = hiddenElement.text().length + line.startIndex; 
      if(hiddenElement.width() > width) { 
       // check whether character after width or before width is closest 
       if(Math.abs(width - prevWidth) < Math.abs(width - elWidth)) { 
        caretIndex = caretIndex -1; // move index back one if previous is closes 
       } 
       return false; 
      } 
     }); 
    } 
    hiddenElement.remove(); 
    return caretIndex; 
} 

che con la seguente funzione keydown è sufficiente per attraversare abbastanza accuratamente tra contentedita ble paragrafi:

$(document).on('keydown', 'p[contenteditable="true"]', function(e) { 
    //if cursor on first line & up arrow key 
    if(e.which == 38 && (cursorIndex() < $(this).lines()[0].text.length)) { 
     e.preventDefault(); 
     if ($(this).prev().is('p')) { 
      prev = $(this).prev('p'); 
      getDistanceToCaret = distanceToCaret($(this), cursorIndex()); 
      lineNumber = prev.lines().length; 
      caretPosition = getCaretViaWidth(prev, lineNumber, getDistanceToCaret); 
      prev.focus(); 
      setCaret(prev.get(0), caretPosition); 
     } 
    // if cursor on last line & down arrow 
    } else if(e.which == 40 && cursorIndex() >= $(this).lastLine().startIndex && cursorIndex() <= ($(this).lastLine().startIndex + $(this).lastLine().text.length)) { 
     e.preventDefault(); 
     if ($(this).next().is('p')) { 
      next = $(this).next('p'); 
      getDistanceToCaret = distanceToCaret($(this), cursorIndex()); 
      caretPosition = getCaretViaWidth(next, 1, getDistanceToCaret); 
      next.focus(); 
      setCaret(next.get(0), caretPosition); 
     } 
     //if start of paragraph and left arrow 
    } else if(e.which == 37 && cursorIndex() == 0) { 
     e.preventDefault(); 
     if ($(this).prev().is('p')) { 
      prev = $(this).prev('p'); 
      prev.focus(); 
      setCaret(prev.get(0), prev.text().length); 
     } 
     // if end of paragraph and right arrow 
    } else if(e.which == 39 && cursorIndex() == $(this).text().length) { 
     e.preventDefault(); 
     if ($(this).next().is('p')) { 
      $(this).next('p').focus(); 
     } 
    }; 
+0

Stavo cercando questa risposta a lungo! Grazie mille! Ho una domanda per te. Ho una struttura di codice diversa, come questa: ci sono molti elenchi, e all'interno degli elenchi c'è un div e dopo div è p (contenteditable). Ho usato il tuo codice, ma non riesco a trovare questo p successivo (contenteditable). Come posso ottenerlo? –

Problemi correlati