javascript internet-explorer dom

javascript - El document.selection.createRange de IE no incluye líneas en blanco iniciales o finales



internet-explorer dom (4)

El movimiento por bazillion negativo parece funcionar perfectamente.

Esto es lo que terminé con:

var sel=document.selection.createRange(); var temp=sel.duplicate(); temp.moveToElementText(textarea); var basepos=-temp.moveStart(''character'', -10000000); this.m_selectionStart = -sel.moveStart(''character'', -10000000)-basepos; this.m_selectionEnd = -sel.moveEnd(''character'', -10000000)-basepos; this.m_text=textarea.value.replace(//r/n/gm,"/n");

Gracias Bobince: cómo puedo votar tu respuesta cuando solo es un comentario :(

Estoy tratando de extraer la selección exacta y la ubicación del cursor desde un área de texto. Como de costumbre, lo que es fácil en la mayoría de los navegadores no está en IE.

Estoy usando esto:

var sel=document.selection.createRange(); var temp=sel.duplicate(); temp.moveToElementText(textarea); temp.setEndPoint("EndToEnd", sel); selectionEnd = temp.text.length; selectionStart = selectionEnd - sel.text.length;

Lo cual funciona el 99% del tiempo. El problema es que TextRange.text no devuelve caracteres de línea nuevos TextRange.text o finales. Entonces, cuando el cursor está en un par de líneas en blanco después de un párrafo, cede una posición al final del párrafo anterior, en lugar de la posición real del cursor.

p.ej:

the quick brown fox| <- above code thinks the cursor is here | <- when really it''s here

La única solución que se me ocurre es insertar temporalmente un personaje antes y después de la selección, tomar la selección real y luego quitar esos caracteres temporales nuevamente. Es un truco, pero en un experimento rápido parece que funcionará.

Pero primero me gustaría estar seguro de que no hay una manera más fácil.


Estoy agregando otra respuesta ya que mi anterior ya se está volviendo un tanto épica.

Esto es lo que considero la mejor versión hasta ahora: toma el enfoque de Bobince (mencionado en los comentarios de mi primera respuesta) y corrige las dos cosas que no me gustaban, que fueron las primeras en que se basa en TextRanges que se pierden fuera del área de texto (lo que perjudica el rendimiento), y en segundo lugar la suciedad de tener que elegir un número gigante para el número de caracteres para mover el límite del rango.

function getSelection(el) { var start = 0, end = 0, normalizedValue, range, textInputRange, len, endRange; if (typeof el.selectionStart == "number" && typeof el.selectionEnd == "number") { start = el.selectionStart; end = el.selectionEnd; } else { range = document.selection.createRange(); if (range && range.parentElement() == el) { len = el.value.length; normalizedValue = el.value.replace(//r/n/g, "/n"); // Create a working TextRange that lives only in the input textInputRange = el.createTextRange(); textInputRange.moveToBookmark(range.getBookmark()); // Check if the start and end of the selection are at the very end // of the input, since moveStart/moveEnd doesn''t return what we want // in those cases endRange = el.createTextRange(); endRange.collapse(false); if (textInputRange.compareEndPoints("StartToEnd", endRange) > -1) { start = end = len; } else { start = -textInputRange.moveStart("character", -len); start += normalizedValue.slice(0, start).split("/n").length - 1; if (textInputRange.compareEndPoints("EndToEnd", endRange) > -1) { end = len; } else { end = -textInputRange.moveEnd("character", -len); end += normalizedValue.slice(0, end).split("/n").length - 1; } } } } return { start: start, end: end }; } var el = document.getElementById("your_textarea"); var sel = getSelection(el); alert(sel.start + ", " + sel.end);


Un complemento jquery para iniciar y finalizar el índice de selección en el área de texto. Los códigos javascript anteriores no funcionaron para IE7 e IE8 y dieron resultados muy inconsistentes, así que escribí este pequeño plugin jquery. Permite guardar temporalmente el índice de inicio y final de la selección y resaltar la selección en otro momento.

Un ejemplo de trabajo y una breve versión está aquí: http://jsfiddle.net/hYuzk/3/

Una versión más detallada con comentarios, etc. está aquí: http://jsfiddle.net/hYuzk/4/

// Cross browser plugins to set or get selection/caret position in textarea, input fields etc for IE7,IE8,IE9, FF, Chrome, Safari etc $.fn.extend({ // Gets or sets a selection or caret position in textarea, input field etc. // Usage Example: select text from index 2 to 5 --> $(''#myTextArea'').caretSelection({start: 2, end: 5}); // get selected text or caret position --> $(''#myTextArea'').caretSelection(); // if start and end positions are the same, caret position will be set instead o fmaking a selection caretSelection : function(options) { if(options && !isNaN(options.start) && !isNaN(options.end)) { this.setCaretSelection(options); } else { return this.getCaretSelection(); } }, setCaretSelection : function(options) { var inp = this[0]; if(inp.createTextRange) { var selRange = inp.createTextRange(); selRange.collapse(true); selRange.moveStart(''character'', options.start); selRange.moveEnd(''character'',options.end - options.start); selRange.select(); } else if(inp.setSelectionRange) { inp.focus(); inp.setSelectionRange(options.start, options.end); } }, getCaretSelection: function() { var inp = this[0], start = 0, end = 0; if(!isNaN(inp.selectionStart)) { start = inp.selectionStart; end = inp.selectionEnd; } else if( inp.createTextRange ) { var inpTxtLen = inp.value.length, jqueryTxtLen = this.val().length; var inpRange = inp.createTextRange(), collapsedRange = inp.createTextRange(); inpRange.moveToBookmark(document.selection.createRange().getBookmark()); collapsedRange.collapse(false); start = inpRange.compareEndPoints(''StartToEnd'', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveStart(''character'', -inpTxtLen); end = inpRange.compareEndPoints(''EndToEnd'', collapsedRange) > -1 ? jqueryTxtLen : inpRange.moveEnd(''character'', -inpTxtLen); } return {start: Math.abs(start), end: Math.abs(end)}; }, // Usage: $(''#txtArea'').replaceCaretSelection({start: startIndex, end: endIndex, text: ''text to replace with'', insPos: ''before|after|select''}) // Options start: start index of the text to be replaced // end: end index of the text to be replaced // text: text to replace the selection with // insPos: indicates whether to place the caret ''before'' or ''after'' the replacement text, ''select'' will select the replacement text replaceCaretSelection: function(options) { var pos = this.caretSelection(); this.val( this.val().substring(0,pos.start) + options.text + this.val().substring(pos.end) ); if(options.insPos == ''before'') { this.caretSelection({start: pos.start, end: pos.start}); } else if( options.insPos == ''after'' ) { this.caretSelection({start: pos.start + options.text.length, end: pos.start + options.text.length}); } else if( options.insPos == ''select'' ) { this.caretSelection({start: pos.start, end: pos.start + options.text.length}); } } });


NB Por favor, consulte mi otra respuesta para la mejor solución que puedo ofrecer. Lo dejo aquí por antecedentes.

Me he encontrado con este problema y he escrito lo siguiente que funciona en todos los casos. En IE, utiliza el método que sugirió para insertar temporalmente un carácter en el límite de selección, y luego usa document.execCommand("undo") para eliminar el carácter insertado y evitar que la inserción permanezca en la pila de deshacer. Estoy bastante seguro de que no hay una manera más fácil. Afortunadamente, IE 9 admitirá las propiedades selectionStart y selectionEnd .

function getSelectionBoundary(el, isStart) { var property = isStart ? "selectionStart" : "selectionEnd"; var originalValue, textInputRange, precedingRange, pos, bookmark; if (typeof el[property] == "number") { return el[property]; } else if (document.selection && document.selection.createRange) { el.focus(); var range = document.selection.createRange(); if (range) { range.collapse(!!isStart); originalValue = el.value; textInputRange = el.createTextRange(); precedingRange = textInputRange.duplicate(); pos = 0; if (originalValue.indexOf("/r/n") > -1) { // Trickier case where input value contains line breaks // Insert a character in the text input range and use that as // a marker range.text = " "; bookmark = range.getBookmark(); textInputRange.moveToBookmark(bookmark); precedingRange.setEndPoint("EndToStart", textInputRange); pos = precedingRange.text.length - 1; // Executing an undo command to delete the character inserted // prevents this method adding to the undo stack. This trick // came from a user called Trenda on MSDN: // http://msdn.microsoft.com/en-us/library/ms534676%28VS.85%29.aspx document.execCommand("undo"); } else { // Easier case where input value contains no line breaks bookmark = range.getBookmark(); textInputRange.moveToBookmark(bookmark); precedingRange.setEndPoint("EndToStart", textInputRange); pos = precedingRange.text.length; } return pos; } } return 0; } var el = document.getElementById("your_textarea"); var startPos = getSelectionBoundary(el, true); var endPos = getSelectionBoundary(el, false); alert(startPos + ", " + endPos);

ACTUALIZAR

Basado en el enfoque sugerido de bobince en los comentarios, he creado lo siguiente, que parece funcionar bien. Algunas notas:

  1. El enfoque de Bobince es más simple y más corto.
  2. Mi enfoque es intrusivo: realiza cambios en el valor de la entrada antes de revertir esos cambios, aunque no hay un efecto visible de esto.
  3. Mi enfoque tiene la ventaja de mantener todas las operaciones dentro de la entrada. El enfoque de Bobince se basa en la creación de rangos que abarcan desde el inicio del cuerpo hasta la selección actual.
  4. Una consecuencia de 3. es que el rendimiento de bobince varía con la posición de la entrada dentro del documento mientras que el mío no. Mis simples pruebas sugieren que cuando la entrada está cerca del inicio del documento, el enfoque de Bobince es significativamente más rápido. Cuando la entrada es posterior a un fragmento significativo de HTML, mi enfoque es más rápido.

function getSelection(el) { var start = 0, end = 0, normalizedValue, textInputRange, elStart; var range = document.selection.createRange(); var bigNum = -1e8; if (range && range.parentElement() == el) { normalizedValue = el.value.replace(//r/n/g, "/n"); start = -range.moveStart("character", bigNum); end = -range.moveEnd("character", bigNum); textInputRange = el.createTextRange(); range.moveToBookmark(textInputRange.getBookmark()); elStart = range.moveStart("character", bigNum); // Adjust the position to be relative to the start of the input start += elStart; end += elStart; // Correct for line breaks so that offsets are relative to the // actual value of the input start += normalizedValue.slice(0, start).split("/n").length - 1; end += normalizedValue.slice(0, end).split("/n").length - 1; } return { start: start, end: end }; } var el = document.getElementById("your_textarea"); var sel = getSelection(el); alert(sel.start + ", " + sel.end);