I have found this question which provides a solution to compute the exact position of the caret in a text or input box.
For my purposes, this is overkill. I only want to know when the caret is at the end of all the text of an input box. Is there an easy way to do that?
In all modern browsers:
//input refers to the text box
if(input.value.length == input.selectionEnd){
//Caret at end.
}
The selectionEnd property of an input element equals the highest selection index.
<script>
input = document.getElementById('yourinputfieldsid');
if(input.selectionEnd == input.selectionStart && input.value.length == input.selectionEnd){
//your stuff
}
</script>
This checks to see if the caret actually is at the end, and makes sure that it isn't only because of the selection that it shows an end value.
You don't specify what you want to happen when some text is selected, so in that case my code just checks whether the end of the selection is at the end of the input.
Here's a cross-browser function that wil work in IE < 9 (which other answers will not: IE only got selectionStart and selectionEnd in version 9).
Live demo: http://jsfiddle.net/vkCpH/1/
Code:
function isCaretAtTheEnd(el) {
var valueLength = el.value.length;
if (typeof el.selectionEnd == "number") {
// Modern browsers
return el.selectionEnd == valueLength;
} else if (document.selection) {
// IE < 9
var selRange = document.selection.createRange();
if (selRange && selRange.parentElement() == el) {
// Create a working TextRange that lives only in the input
var range = el.createTextRange();
range.moveToBookmark(selRange.getBookmark());
return range.moveEnd("character", valueLength) == 0;
}
}
return false;
}
Related
I'm creating a markdown editor and I need to check if neighbor characters are specific characters, then remove them, else append them.
For e.g I want to check selected-text, tow neighbor characters are **, then remove them, else append them around selected text.
I can get selected text using this approach:
function getSelection(elem) {
var selectedText;
if (document.selection != undefined) { // IE
elem.focus();
var sel = document.selection.createRange();
selectedText = sel.text;
} else if (elem.selectionStart != undefined) { // Firefox
var startPos = elem.selectionStart;
var endPos = elem.selectionEnd;
selectedText = elem.value.substring(startPos, endPos)
}
return selectedText;
}
$(document).on('mousedown', 'button', function(e) {
var selection = getSelection( $('#txtarea').get(0) );
alert(selection);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea id="txtarea">this is a test</textarea>
<button>Bold (toggle)</button>
Now I need when user clicks on that button, it checks if selected text is between ** like this **selectedtext**, then remove them like this selected text else append them like this **selectedtext**. How can I do that?
Before anything I would like to refer to all the markdown editors out there: https://www.google.de/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=javascript%20markdown%20library
So: do not try to reinvent the the wheel, and so on.
But for the sake of learning, my approach would look like this:
function toggleMarker(marker, el) {
var markerLength = marker.length;
var startPos, endPos, selection, range;
if (document.selection != undefined) { // IE
el.focus();
range = document.selection.createRange();
selection = range.text;
} else if (el.selectionStart != undefined) { // Firefox
startPos = el.selectionStart;
endPos = el.selectionEnd;
selection = el.value.substring(startPos, endPos);
}
if (!selection.length){
return;
}
if (el.value.substring(startPos-markerLength,startPos) === marker
&& el.value.substring(endPos,endPos+markerLength) === marker
){
el.value = el.value.substring(0,startPos-markerLength) +
selection +
el.value.substring(endPos+markerLength);
}
else{
el.value = el.value.substring(0,startPos) + marker +
selection + marker + el.value.substring(endPos);
}
}
$(document).on('mousedown', 'button', function(e) {
toggleMarker( $(this).data('marker'), $('#txtarea').get(0) ).text;
});
See it in action: https://jsfiddle.net/t4ro53v8/4/
The solution takes a very generic approach: the marker to toggle is set as a custom data attribute to make it easy to reuse the code.
The functionality is only implemented for the non-IE case. You will have to check, how to determine startPos and endPos for a range in IE.
In all other browsers:
the selection is identified
nothing is done if nothing is selected
sourroundings of the selection are checked against the given marker
if both markers are present, they get deleted
otherwise the markers are inserted
As a proof of concept this example works like a charm.
But there are some shortcomings:
How to distinguish between bold text(**) and italics(*)?
How to handle markers that just appear just on one side of the selection
What to do, if a marker is selected?
But that is for you to solve now ...
You could use regex to find the occurance of a ** ** pattern.This regex will help you find the pattern similar to what you have.
[*][*][a-z]*[*][*] .
Using the exec() method, will help you extract that particular text.
Check the length of this using .length, if it is 4, then there is nothing in between, and you can replace it with the new text surrounded by **,
"**"+ newtext+"**"
For removing the **, you can use the replace() method, where you replace ** with whitespace or so.
Within a contenteditable DIV, I want to determine, if the user made a selection from left-to-right or from right-to-left. Does someone have a Javascript solution for the Browsers Firefox, Chrome, Safari, Opera? And when possible, also one for IE?
<div id="editor" contenteditable>
Selection from Cursor end | here <strong>over bold</strong> to Cursor start | here.
</div>
I prepared the code in jsFiddle here: http://jsfiddle.net/ecUka/
Thank's in advance :-)
Here's a function that uses the fact that setting the end of a DOM Range to be at an earlier point in the document than the start of the range will collapse the range.
Demo: http://jsfiddle.net/97MDR/17/
Code:
function isSelectionBackwards() {
var backwards = false;
if (window.getSelection) {
var sel = window.getSelection();
if (!sel.isCollapsed) {
var range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
backwards = range.collapsed;
}
}
return backwards;
}
Demo:
function isSelectionBackwards() {
var backwards = false;
if (window.getSelection) {
var sel = window.getSelection();
if (!sel.isCollapsed) {
var range = document.createRange();
range.setStart(sel.anchorNode, sel.anchorOffset);
range.setEnd(sel.focusNode, sel.focusOffset);
backwards = range.collapsed;
}
}
return backwards;
}
document.addEventListener("selectionchange", function() {
document.getElementById("selReport").textContent = isSelectionBackwards();
});
<p>Select something forwards and backwards in here</p>
<p>Selection is backwards: <strong id="selReport">false</strong><p>
Unless you need to support Internet Explorer <9, which seems highly unlikely in 2023, you can safely ignore the rest of this answer, which I'm leaving here for posterity.
The above works in all major browsers except IE < 9, which does not support the same Range and Selection APIs as other browsers.
For IE < 9, there is simply nothing in the selection API to tell you about the selection direction. The best I can suggest is using the selectionchange event to keep track of the previously selected range and see which end has changed each time it fires. It seems to work in the following example but has had no testing apart from that, so use at your own risk.
Demo: http://jsfiddle.net/97MDR/18/
Additional code specific to IE < 9 that won't work in modern browsers:
var selectedRange, selectionBackwards;
document.onselectionchange = function(evt) {
evt = evt || window.event;
var sel = document.selection;
if (sel && sel.type !== "Control") {
if (sel.type == "Text") {
// Selection is not collapsed, so compare range end points
var newRange = sel.createRange();
if (selectedRange) {
var startChanged = (newRange.compareEndPoints("StartToStart", selectedRange) != 0);
var endChanged = (newRange.compareEndPoints("EndToEnd", selectedRange) != 0);
if (startChanged && !endChanged) {
selectionBackwards = true;
} else if (endChanged && !startChanged) {
selectionBackwards = false;
} else if (startChanged && endChanged) {
// Both ends have changed, which is confusing.
// I suspect this can happen when the selection snaps
// to words. In this case we can tell nothing, so leave
// selectionBackwards alone.
} else {
// Neither end has changed, so we can tell nothing.
}
}
selectedRange = newRange;
} else {
// Selection is collapsed
selectionBackwards = false;
}
}
};
range.setStart(anchorNode, anchorOffset);
range.setEnd(focusNode, focusOffset);
if(range.collapsed == false){
colsole.log('forward Selected');
}else{
colsole.log('backward Selected');
}
I am trying to create a very basic rich-text editor in JavaScript but I'm having an issue with selections. So basically, since it's a contentEditable <div>, any time the user pastes in pre-formatted text from a webpage, the formatting doesn't get stripped off.
An easy to break hack is to give focus to a <textarea> upon Ctrl + V being pressed, so the text gets pasted in there, then onkeyup, give focus back to the <div>, copy the contents over and delete whatever went in to the <textarea>.
So that's easy, but when I give focus back to the contentEditable &;t;div>, the caret position is at the beginning and not immediately after the paste. I don't know enough about selections and whatnot to figure it out, so I'd appreciate some help. Here's my code:
// Helpers to keep track of the length of the thing we paste, the cursor position
// and a temporary random number so we can mark the position.
editor_stuff =
{
cursor_position: 0,
paste_length: 0,
temp_rand: 0,
}
// On key up (for the textarea).
document.getElementById("backup_editor").onkeyup = function()
{
var main_editor = document.getElementById("question_editor");
var backup_editor = document.getElementById("backup_editor");
var marker_position = main_editor.innerHTML.search(editor_stuff.temp_rand);
// Replace the "marker" with the .value of the <textarea>
main_editor.innerHTML = main_editor.innerHTML.replace(editor_stuff.temp_rand, backup_editor.value);
backup_editor.value = "";
main_editor.focus();
}
// On key down (for the contentEditable DIV).
document.getElementById("question_editor").onkeydown = function(event)
{
key = event;
// Grab control + V end handle paste so "plain text" is pasted and
// not formatted text. This is easy to break with Edit -> Paste or
// Right click -> Paste.
if
(
(key.keyCode == 86 || key.charCode == 86) && // "V".
(key.keyCode == 17 || key.charCode == 17 || key.ctrlKey) // "Ctrl"
)
{
// Create a random number marker at the place where we paste.
editor_stuff.temp_rand = Math.floor((Math.random() * 99999999));
document.getElementById("question_editor").textContent += editor_stuff.temp_rand;
document.getElementById("backup_editor").focus();
}
}
So my thinking is to store the cursor position (integer) in my helper array (editor_stuff.cursor_position).
N.B. I've been looking at other answers on SO all day and can't get any of them to work for me.
Here's a function that inserts text at the caret position:
Demo: http://jsfiddle.net/timdown/Yuft3/2/
Code:
function pasteTextAtCaret(text) {
var sel, range;
if (window.getSelection) {
// IE9 and non-IE
sel = window.getSelection();
if (sel.getRangeAt && sel.rangeCount) {
range = sel.getRangeAt(0);
range.deleteContents();
var textNode = document.createTextNode(text);
range.insertNode(textNode);
// Preserve the selection
range = range.cloneRange();
range.setStartAfter(textNode);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
} else if (document.selection && document.selection.type != "Control") {
// IE < 9
document.selection.createRange().text = text;
}
}
I have text boxes <input type='text'> that only allow numeric characters and wont let the user enter a dot (.) more than once. Problem is, if the text in the text box is selected, the user intends to overwrite the contents with a dot, hence making it allowed! The question is, how can you tell in javascript whether the text in that text box is selected or not.
Thanks
The following will tell you whether or not all of the text is selected within a text input in all major browsers.
Example: http://www.jsfiddle.net/9Q23E/
Code:
function isTextSelected(input) {
if (typeof input.selectionStart == "number") {
return input.selectionStart == 0 && input.selectionEnd == input.value.length;
} else if (typeof document.selection != "undefined") {
input.focus();
return document.selection.createRange().text == input.value;
}
}
2017 Specific Answer - Faced the same issue recently.
We were allowing users to enter only 3 digits at a time. When the user tried to enter the fourth character we returned false.
This became an issue when the user had a selection and was trying to overwrite the values.
Taking a hint from Tim's answer. I understood that I wanted to see if the selection value was same as the input's value.
In modern browsers I achieved it by doing:
document.getSelection().toString() === input.value // edited
Hope this helps someone.
For anyone who needs the code to get at the selected text within a textbox, here's an enhanced version:
http://jsfiddle.net/9Q23E/527/
function getSelection(textbox)
{
var selectedText = null;
var activeElement = document.activeElement;
// all browsers (including IE9 and up), except IE before version 9
if(window.getSelection && activeElement &&
(activeElement.tagName.toLowerCase() == "textarea" || (activeElement.tagName.toLowerCase() == "input" && activeElement.type.toLowerCase() == "text")) &&
activeElement === textbox)
{
var startIndex = textbox.selectionStart;
var endIndex = textbox.selectionEnd;
if(endIndex - startIndex > 0)
{
var text = textbox.value;
selectedText = text.substring(textbox.selectionStart, textbox.selectionEnd);
}
}
else if (document.selection && document.selection.type == "Text" && document.selection.createRange) // All Internet Explorer
{
var range = document.selection.createRange();
selectedText = range.text;
}
return selectedText;
}
Instead of hitting the wall of digits dots and selections you can climb it easily by checking the value in onchange event.
HTML:
<input type="text" onchange="ValidateNumericValue(this);" />
JS:
function ValidateNumericValue(oInput) {
var blnRequired = true; //set to false if allowing empty value
var sValue = oInput.value;
if (blnRequired && sValue.length == 0) {
alert("Please enter a value");
oInput.focus();
return;
}
var numericValue = parseFloat(sValue);
if (isNaN(numericValue)) {
alert("Value is not a valid number");
oInput.focus();
return;
}
//put back to make 2.15A back to 2.15
oInput.value = numericValue + "";
}
This will check the value when changed (and user go to different element) and when not valid will alert and set focus back.
Live test case: http://jsfiddle.net/yahavbr/NFhay/
You can get the id of the selected element in the page with the following code:
elem_offset = document.getSelection().anchorOffset;
elem = document.getSelection().anchorNode.childNodes[elem_offset];
alert(elem.id);
If you're use case is simply to know whether any text is selected.
The difference between selectionStart and selectionEnd is always zero when no text is selected irrespective of cursor position.
So this should do the trick
const element = document.getElementById('inputbox');
const isTextSelected = element.selectionStart - element.selectionEnd;
I have these two codes -
new function($) {
$.fn.getCursorPosition = function() {
var pos = 0;
var el = $(this).get(0);
// IE Support
if (document.selection) {
el.focus();
var Sel = document.selection.createRange();
var SelLength = document.selection.createRange().text.length;
Sel.moveStart('character', -el.value.length);
pos = Sel.text.length - SelLength;
}
// Firefox support
else if (el.selectionStart || el.selectionStart == '0')
pos = el.selectionStart;
return pos;
}
} (jQuery);
And
var element = document.getElementById('txtarr');
if( document.selection ){
// The current selection
var range = document.selection.createRange();
// We'll use this as a 'dummy'
var stored_range = range.duplicate();
// Select all text
stored_range.moveToElementText( element );
// Now move 'dummy' end point to end point of original range
stored_range.setEndPoint( 'EndToEnd', range );
// Now we can calculate start and end points
element.selectionStart = stored_range.text.length - range.text.length;
element.selectionEnd = element.selectionStart + range.text.length;
}
The first one is for getting the cursor position in a textarea and the second one is for determining the end of a textarea ,but they give the same result?
Where's the mistake?
I fix it.It's very simple :) .
I just replace the second code(for determining the end of the textarea) with:$("#txtarr").val().length(jQuery).#txtarr is the id of mine textarea.
Both pieces of code are doing the same thing in slightly different ways. Each is attempting to get the position of the caret or selection in a textarea (or text input), although the first only gets the start position of the selection while the second gets both the start and end positions.
Both have flaky inferences: the first assumes a browser featuring document.selection will support TextRange, while the second makes the same inference plus another that assumes a browser without support for document.selection will have support for selectionStart and selectionEnd properties of textareas. Neither will correctly handle line breaks in IE. For code that does that, see my answer here: How to get the start and end points of selection in text area?