How can I get at the content of a text area and swap the two characters around the cursor, in Javascript? I want to write a tiny, but useful Chrome extension that will let me do this when I mistype in gmail. (I'm assuming that the the main editing area in gmail is a textarea).
This may be a question too stupid to ask here. In any case, I have searched for an answer and failed. I'm not a real programmer, but I have written snippets to do this in other scripting languages. It's easy enough to do it Firefox, for example, with an autohotkey script. For some reason the Javascript quite defeats me.
See the code in http://jsfiddle.net/Lg3Ng/
HTML:
<textarea id="my_text" onclick="changeChar();"></textarea>
JAVASCRIPT:
function changeChar() {
var pos = getCaretPosition(document.getElementById("my_text"));
//alert(pos);
if (pos > 0) swapChars(pos - 1);
}
function swapChars(pos) {
var cur_val = document.getElementById("my_text").value;
var firstChar = cur_val.charAt(pos);
var secondChar = cur_val.charAt(pos + 1);
var startString = cur_val.substr(0, pos);
var endString = cur_val.substring(pos + 2);
document.getElementById("my_text").value = startString + secondChar + firstChar + endString;
}
// From http://demo.vishalon.net/getset.htm
function getCaretPosition(ctrl) {
var CaretPos = 0;
// IE Support
if (document.selection) {
ctrl.focus();
var Sel = document.selection.createRange();
Sel.moveStart('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
else if (ctrl.selectionStart || ctrl.selectionStart == '0'){
CaretPos = ctrl.selectionStart;
}
return (CaretPos);
}
Related
Is it possible to diversify the font size only in some parts of the text in a textarea with Javascript?
custom_button.onclick = function () {
// IE version
if (document.selection != undefined)
{
component_name.focus();
var sel_ie = document.selection.createRange();
textNew = sel_ie.text;
}
// Mozilla version
else
{
var startPos = document.getElementById(component_name).selectionStart;
var endPos = document.getElementById(component_name).selectionEnd;
textNew = document.getElementById(component_name).value.substring(startPos, endPos);
}
var finalText = text.substr(0,startPos) + '<font size="100">' + textNew + '</font>' + text.substr(endPos);
}
When I usually write the code above I achieve the following result into the textarea:
This <font size="100">is the</font> text
I have a div with the contenteditable attribute. The user needs to be able to type and insert several select menus where the cursor is. I've managed to get the cursor position and to insert the first select menu, but it only works on the first text node.
That's how I get the cursor position:
function getCaretCharacterOffsetWithin(element) {
var caretOffset = 0;
var doc = element.ownerDocument || element.document;
var win = doc.defaultView || doc.parentWindow;
var sel;
if (typeof win.getSelection != "undefined") {
sel = win.getSelection();
if (sel.rangeCount > 0) {
var range = win.getSelection().getRangeAt(0);
var preCaretRange = range.cloneRange();
preCaretRange.selectNodeContents(element);
preCaretRange.setEnd(range.endContainer, range.endOffset);
caretOffset = preCaretRange.toString().length;
}
} else if ( (sel = doc.selection) && sel.type != "Control") {
var textRange = sel.createRange();
var preCaretTextRange = doc.body.createTextRange();
preCaretTextRange.moveToElementText(element);
preCaretTextRange.setEndPoint("EndToEnd", textRange);
caretOffset = preCaretTextRange.text.length;
}
return caretOffset;
}
Then I update it every time the user types or clicks.
function updatePos() {
var el = document.getElementById("msg");
pos = getCaretCharacterOffsetWithin(el);
}
document.body.onkeyup = updatePos;
document.body.onmouseup = updatePos;
Then here's how I'm handling the button that adds the select. I'm not sure how to insert an element after a text node, so I insert a br tag and remove it later. There has to be a cleaner way, right?
$('#btn').click(function(){
var selectList = document.createElement('select');
var msg = $('#msg');
$(msg).html(function(){
var first = $(msg).html().substring(0, pos);
var last = $(msg).html().substring(pos);
return first + '<br>' + last;
});
$(msg).contents().filter('br').after(selectList);
$(msg).contents().filter('br').remove();
$(msg).focus();
})
I guess the problem is that I'm using substring to split the text and be able to insert the select there, and as soon as there is another select tag, the substring is not able to go past the first text node. So maybe I'm supposed to redo the whole thing with a different approach, but I'm completely stuck.
Here's the jsfiddle: https://jsfiddle.net/8a63sosr/
Thanks!
The problem is that for HTML elements it takes, well, in terms of jQuery, text(), not html().
I guess there's a better solution with some range parameters, but here's something:
//fix caret pos
var temlOffset = pos;
$('#msg').html().split(/(<[^>]+>)/g).forEach(function(el){
if(temlOffset > 0){
if(el.length && el[0] === '<'){
pos += el.length;
} else {
temlOffset -= el.length;
}
}
});
https://jsfiddle.net/Lemz17L8/
So, what it does is adding the html tag length to the pos value.
Best regards, Alexander
can we get the word on which a right click was made and x,y coordinates of that word ?
i tried:
document.onclick=getTextOnClick;
function getTextOnClick(e)
{
console.log(e);
if (window.getSelection) {
txt = window.getSelection();
console.log(">>>"+txt);
} else if (document.getSelection) {
// FireFox
txt = document.getSelection();
console.log(txt);
} else if (document.selection) {
// IE 6/7
txt = document.selection.createRange().text;
console.log(txt);
}
}
Now this code works if i select some text, but can i get the same when i just eight click or click on certain word ? And event object is giving me coordinates of click. can i get coordinates of the word on which the click was made ? Plz help
This can be done with pure JavaScript, assuming your container contain "simple" words only:
window.onload = function() {
var oDiv = document.getElementById("Container");
var rawHTML = oDiv.innerHTML;
var arrWords = rawHTML.split(" ");
oDiv.innerHTML = "";
for (var i = 0; i < arrWords.length; i++) {
var curWord = arrWords[i];
var curSpan = document.createElement("span");
curSpan.innerHTML = curWord;
if (i < (arrWords.length - 1))
curSpan.innerHTML += " ";
curSpan.onclick = WordClicked;
curSpan.oncontextmenu = WordClicked;
oDiv.appendChild(curSpan);
}
};
function WordClicked() {
var word = this.innerHTML;
alert("You clicked: " + word);
return false;
}
Live test case - handles both left and right click.
One way that comes to mind is putting each word in a span of its own. Apart from that, I think it will be difficult to find a solution that runs consistently on all browsers.
Thy this ?
Text ->
<div id="foo">Hello I am a text. I have no clue why I am here or
what I am good for, but I have QUOTES and other things I can offer.</div>
jQuery ->
$('#foo').contents().each(function(_, node) {
var nodelist = node.textContent.split(/\s/).map(function( word ) {
return $('<span>', {
text: word + ' ',
click: function() {
alert( $(this).text() );
}
}).get(0);
});
$('#foo').empty().append(nodelist);
});
Demo -> http://jsfiddle.net/qLuEF/
What Im trying to do is - i dont know the name maybe - a Prediction Help Inputter inside a textarea. It uses jquery autocomplete. When the user types '[[g ' inside textarea (id=test), a input with autocomplete is opened (id=example), so it search in 'data'. When the user find the desired data, he must press Shif+Enter to insert the data into the textarea, closing with ']]'.
How could I find the position of the carret to make the input appears near there?
I dont want to find the index of the carret, but something like the x y absolute position.
What do you suggest me?
Code above:
<textarea onkeydown="predicao(this);" cols="40" rows="10" id="test" onfocus="this.focus()"></textarea>
<input id="example" style="display: none;" onkeyup="insert(this, event);"/>
<script language="Javascript">
<!--
function predicao(objeto){
comprimento = objeto.value.length;
var antipenultimo = comprimento - 4;
var input = objeto.value.substring(antipenultimo,comprimento);
var output = "";
for(i=0; i<input.length; ++i){
if(output != "") output += ", ";
output += input.charCodeAt(i);
}
if (output == "91, 91, 103, 32"){
var preditor = document.getElementById('example');
preditor.value = '';
preditor.style.display = 'block';
preditor.focus();
preditor.select();
}
}
function insert(objeto, evt){
var e = evt || event;
var code = e.keyCode || e.which;
if(e.shiftKey && code == '13') {
var texto = document.getElementById('test').value;
texto += objeto.value+']]';
document.getElementById('test').focus();
document.getElementById('test').value = texto;
objeto.style.display = 'none';
}
}
$(document).ready(function(){
var data = "Afrikaans Català Deutsch English Esperanto Suomi Français Galego Hrvatski Magyar Bahasa Indonesia Italiano Basa Jawa".split(" ");
$("#example").autocomplete(data);});
</script>
Something like:
var pos = $('textarea').caret(); // using caret plugin...
var lines = $('textarea').val().slice(0, pos).replace(/\t/g, ' ').split('\n');
var y = lines.length;
var x = lines[y-1].length;
Should work reasonably well for fix-width fonts.
If you're looking to do it via JS only:
function doGetCaretPosition (ctrl) {
var CaretPos = 0; // IE Support
if (document.selection) {
ctrl.focus ();
var Sel = document.selection.createRange ();
Sel.moveStart ('character', -ctrl.value.length);
CaretPos = Sel.text.length;
}
// Firefox support
else if (ctrl.selectionStart || ctrl.selectionStart == '0')
CaretPos = ctrl.selectionStart;
return (CaretPos);
}
Courtesy: http://blog.vishalon.net/index.php/javascript-getting-and-setting-caret-position-in-textarea/
Demo: http://demo.vishalon.net/getset.htm
This is the code (now is full):
HTML:
<div id="content" contentEditable="true" onkeyup="highlight(this)">This is some area to type.</div>
Javascript:
function highlight(elem){
// store cursor position
var cursorPos=document.selection.createRange().duplicate();
var clickx = cursorPos.getBoundingClientRect().left;
var clicky = cursorPos.getBoundingClientRect().top;
// copy contents of div
var content = elem.innerHTML;
var replaceStart = '';
var replaceEnd = '';
// only replace/move cursor if any matches
// note the spacebands - this prevents duplicates
if(content.match(/ test /)) {
elem.innerHTML = content.replace(/ test /g,' '+replaceStart+'test'+replaceEnd+' ');
// reset cursor and focus
cursorPos = document.body.createTextRange();
cursorPos.moveToPoint(clickx, clicky);
cursorPos.select();
}
}
Just woks on IE, unhapply.
Anyone can 'adjust' this code, to work on FF too!...
Thanks
Edit[1]:
Div Editable and More... More
This code replaces a especific word by the same word formatted...
And the caret (cursor) stay always after the word replaced! <<< "This is the big"
But just works on IE, and I like so much to rewrite this code to work on FF... but I can't do it... Its so hard...
Anyone can help?
Edit[2]:
My problem is just with this part:
// reset cursor and focus
cursorPos = document.body.createTextRange();
cursorPos.moveToPoint(clickx, clicky);
cursorPos.select();
Because, moveToPotion and select functions just works on IE... Until then it is easy...
On FF there is another set of functions that make it possible... But i don't know how to write another code that do the same things. Do you got it?
You can preserve the caret position by inserting a marker element at its current location before doing your replacement on the element's innerHTML. (Using DOM methods to traverse the text nodes and searching each for the text you want would be preferable to using innerHTML, by the way).
The following works, so long as the caret is not positioned within or adjacent to the word "text". I also added a timer to prevent calling this function every time a key is pressed and to wait for the user to stop typing for half a second.
function insertCaretMarker() {
var range;
var markerId = "sel_" + new Date() + "_" + ("" + Math.random()).substr(2);
if (window.getSelection) {
var sel = window.getSelection();
range = sel.getRangeAt(0);
range.collapse(true);
var markerEl = document.createElement("span");
markerEl.appendChild(document.createTextNode("\u00a0"));
markerEl.id = markerId;
range.insertNode(markerEl);
} else if (document.selection && document.selection.createRange) {
range = document.selection.createRange();
range.collapse(true);
if (range.pasteHTML) {
range.pasteHTML("<span id=\"" + markerId + "\"> </span>");
}
}
return markerId;
}
function restoreCaret(markerId) {
var el = document.getElementById(markerId);
var range;
if (el) {
if (window.getSelection && document.createRange) {
var sel = window.getSelection();
range = document.createRange();
range.setStartBefore(el);
sel.removeAllRanges();
sel.addRange(range);
} else if (document.body.createTextRange) {
range = document.body.createTextRange();
range.moveToElementText(el);
range.collapse(true);
range.select();
}
el.parentNode.removeChild(el);
}
}
function preserveCaretPosition(func) {
var id = insertCaretMarker();
func();
restoreCaret(id);
}
var highlightTimer;
function highlight(elem) {
if (highlightTimer) {
window.clearTimeout(highlightTimer);
}
highlightTimer = window.setTimeout(function() {
highlightTimer = null;
var replaceStart = '<b>';
var replaceEnd = '</b>';
// only replace/move cursor if any matches
// note the spacebands - this prevents duplicates
if (elem.innerHTML.match(/ test /)) {
preserveCaretPosition(function() {
elem.innerHTML = elem.innerHTML.replace(/ test /g, ' ' + replaceStart + 'test' + replaceEnd + ' ');
});
}
}, 500);
}