Detecting that a textarea is focused during a button click - javascript

Explanation
I have a textarea and a button. When I click the button, I want to insert text into the textarea. However, the text that I insert depends upon the current focus of the texteara. Here are some cases:
Cases
(As of the time that the button is clicked)
Textarea focused
Insert text where the cursor is, as-is
Textarea unfocused
Insert text at the end of the textarea (ie add a newline to the inserted text)
Example / Attempt
Here is a fiddle with my example implementation:
https://jsfiddle.net/reL9ro6L/1/
$(document).ready(function() {
$('button').click(function() {
var $text = $('textarea');
var currentValue = $text.val(),
len = currentValue.length,
isTextAreaFocused = $text.is(':focus'),
optionalNewline = isTextAreaFocused ? '' : '\n';
var start = $text[0].selectionStart,
end = $text[0].selectionEnd,
beforeText = isTextAreaFocused ? currentValue.substring(0, start) : len,
afterText = isTextAreaFocused ? currentValue.substring(end) : len;
var insertedText = 'foo',
newValue = beforeText + insertedText + afterText + optionalNewline;
$text.val(newValue);
});
})
Problem
I believe that the button focuses before it has a chance to know if the textarea is focused. Is there a hook or way to handle the click event on the button such that I'll know (before it is focused) what is focused?
Off point: I'm using Ember as my framework. I'd really love to see a pure JS / jQuery solution, but I just wanted to place Ember on the table as well.

You'd have to use the mousedown event on the button, as it fires before the textarea loses focus.
By the time a click event fires, the mouse has been pressed down, and released, and the focus will have shifted to the button instead.
$(document).ready(function() {
$('button').on({
mousedown: function() {
var text = $('textarea').get(0),
currentValue = text.value,
isTextAreaFocused = text === document.activeElement,
insertedText = 'foo',
start = text.selectionStart,
end = text.selectionEnd,
beforeText = currentValue.substring(0, start) || "",
afterText = currentValue.substring(end) || "",
newValue = beforeText + insertedText + afterText;
text.value = isTextAreaFocused ? newValue : currentValue + insertedText + '\n';
$(this).data({'focus' : isTextAreaFocused, 'end' : end + insertedText.length});
},
mouseup: function() {
if ( $(this).data('focus') ) {
$('textarea').focus().get(0).setSelectionRange($(this).data('end'), $(this).data('end'));
}
}
});
});
textarea {
width: 20em;
height: 10em;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea></textarea>
<button>Insert text</button>

Related

Replace CharacterData content with some HTML

First Time I am asking something here I hope I will be precise enough.
I am trying to make a simple extension for chrome that highlight the selected text when I press "H" on my keyboard, but I have some issue :
Use Case
The user select with his mouse a piece of text.
The user press H on his keyboard
Bibbidi-Bobbidi-Boo the selected text is highlighted.
Code so far
To detect when user press H and to get the piece of text he selected :
window.addEventListener('keydown', e => {
if(e.code == "KeyH")
{
var selected = window.getSelection()
//SimpleHighLight(selected);
ComplexHighLight(selected);
}
});
I have coded coded a simple way to do what I want like this :
function SimpleHighLight(selected){
var selectedText = selected.toString();
if(selectedText.length != 0)
{
var range = selected.getRangeAt(0);
var element = selected.anchorNode.parentNode;
var highlited = "<span style='background: rgb(255,255,0)'>" + selectedText + "</span>";
var reg = new RegExp(selectedText,"g");
var text = element.innerHTML.replace(reg, highlited);
element.innerHTML = text;
}
}
It work fine for piece of text in an unique DOM element and when there is no other occurrence of the selected text but I want it to always work, like in a case of my selected text comes from 2 different paragraphs.
So I did this :
function ComplexHighLight(selected){
var selectedText = selected.toString();
if(selectedText.length != 0)
{
console.log(" Selection : " + selectedText);
var range = selected.getRangeAt(0);
if(!range.collapsed)
{
var startNode = range.startContainer;
var startOffset = range.startOffset;
var endNode = range.endContainer;
var endOffset = range.endOffset;
if(startNode == endNode) //Means that its in the same node element
{
var highlited = "<span style='background: rgb(255,255,0)'>" + selectedText + "</span>";
startNode.replaceData(startOffset, endOffset-startOffset, highlited);
startNode.parentNode.innerHTML = startNode.nodeValue;
}
}
}
}
That's only a part of the problem where I handle when a piece of text is in the same element (I am already too much in trouble to handle when the selected text comes from multiples elements :( ).
Issue
On paper, it should work, but the main issue is that when I do :
startNode.parentNode.innerHTML = startNode.nodeValue;
the <span> division is given to innerHTML as a string and not some HTML stuff.
I have worked around this for about the whole evening but I can't fix it, does anyone have an idea of how I should do that ?

How to select the previously "current active" element? Is there document.hadFocus?

How do I select the one of many input elements that had the cursor in it a split second before clicking a button to add something to the (previously) active input text box? Is there any method document.hadFocus? Can I get the id of the input using onFocusOut and pass it to my function?
I've seen many solutions in JQuery but can anyone do it in vanilla JS?
function insertAtCursor(newText, el = document.activeElement) {
//getElementsByTagName("input")[0].focus();
const start = el.selectionStart;
const end = el.selectionEnd;
const text = el.value;
const before = text.substring(0, start);
const after = text.substring(end, text.length);
el.value = (before + newText + after);
el.selectionStart = el.selectionEnd = start + newText.length;
el.focus();
}
I resolved this by replacing the onclick eventListener of my button (for adding text to an input box...)
with a focusout eventlistener on the previous element (the input box).
That way the event was tied to the input box and had everything I needed.
e.relatedTarget is the opposite of what I needed ("hadFocus"). It is "what element (if any) is getting the new focus of this focusout event?" The if statement uses this relatedTarget to make sure that the focus was lost to the button.
this is tied to the inputBox that "hadFocus" when the button was pressed.
inputBox.addEventListener( "focusout", insertAtCursor )
function insertAtCursor( event ){
if ( e.relatedTarget?.parentElement?.classList?.value == "buttonSet" ){
const text = this.value;
const start = this.selectionStart;
const end = this.selectionEnd;
const wordId = this.id;
const newTextToAddFromButton = e.relatedTarget.innerText;
...
}
}

Clicking outside of textarea loses caret position

I created a textarea and a button. When the button is clicked, I want to add the letter 'a' at the current position of the cursor in the textarea. Below is my current code:
$('button.buttonA').click(function(){
var cursorPos = $('textarea.formInsideMenu').prop('selectionStart');
var textCurrent = $('textarea.formInsideMenu').val();
var textBefore = textCurrent.substring(0, cursorPos);
var textAfter = textCurrent.substring(cursorPos, textCurrent.length);
$('textarea.formInsideMenu').val(textBefore + 'a' + textAfter);
});
The above code works fine, (inserts an 'a' at the correct position), when the focus is on the textarea; but as soon as I click on the button, I lose focus of the textarea and the cursor is no longer showing. If I click on the button again after this, 'a' is appended at the very end of the text, (it seems like the cursor is moved to the end of the text). Is there anyway to keep track of where the cursor is inside the textarea even when something else has been clicked on and the textarea has lost focus?
Once you're done with the insert, you need to focus the textarea and set the caret position back:
$('button.buttonA').click(function() {
var area = $('textarea.formInsideMenu'),
curPos = area.prop('selectionEnd');// at the caret **or after selected text**
area.val( area.val().substring(0, curPos) + 'a' + area.val().substring(curPos) )
.focus()
.prop({'selectionStart': curPos+1, 'selectionEnd': curPos+1});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<button class="buttonA">Add an a</button> <br>
<textarea class="formInsideMenu"></textarea>
This version uses javascript only after returning the dom object from jQuery:
$('button.buttonA').click(function(){
var text = $('textarea.formInsideMenu').get(0);
var start = text.selectionStart;
text.value = text.value.slice(0,start) + 'a' + text.value.slice(start);
text.setSelectionRange(start+1,start+1);
text.focus();
});
Fiddle here
Use setSelectionRange after inserting the 'a'.
$('button.buttonA').click(function(){
var cursorPos = $('textarea.formInsideMenu').prop('selectionStart');
var textCurrent = $('textarea.formInsideMenu').val();
var textBefore = textCurrent.substring(0, cursorPos);
var textAfter = textCurrent.substring(cursorPos, textCurrent.length);
$('textarea.formInsideMenu').val(textBefore + 'a' + textAfter);
var elem = document.getElementsByClassName("formInsideMenu")[0];
elem.setSelectionRange(cursorPos, cursorPos + 1);
});
https://jsfiddle.net/ny82n5kn/
You can make a change event on the textarea, where you sore the position of the cursor, like this:
var cursorPos;
$('textarea.formInsideMenu').on('change', function(){
cursorPos = $(this)).prop('selectionStart');
};
Now it will be avaiable in the clickhandler.

How can i find out which character gets deleted in a tinymce editor on backspace (before it actually gets deleted)?

Assuming the cursor position in a tinymce editor is inside a paragraph.
When a user hits backspace i need to know which character will get deleted.
It is necessary to know this before the character gets removed (onKeyDown is ok, onKeyUp is too late).
How can i find out which character gets deleted on backspace (before it actually gets deleted)?
The code above doesn't take into account backspacing in the middle of a paragraph, or backspacing a whole selection. Try something like the a-tools plugin (although there are several others like it) in combination with the following event handler:
jQuery('input, textarea').keydown(function(e) {
if(e.keyCode === 8) {
var selection = jQuery(this).getSelection();
var selStart = (selection.length) ? selection.start : selection.start - 1;
var selEnd = selection.end;
alert(jQuery(this).val().slice(selStart, selEnd));
}
});
in one of my plugins i set onKeyDown
ed.onKeyDown.add(function(ed, evt) {
if (paragraph && evt.keyCode == 8 && ed.selection.isCollapsed()) {
//insert special marker char
var value = '<span id="__ircaret" class="ircaret">\u2060</span>';
ed.selection.setContent(value, {format : 'raw', no_events: 1});
// node is the dom node the caret is placed in
var node = ed.selection.getNode();
var node_content = $(node).text();
var position = node_content.search('\u2060');
// this is the character
var char_vor_cursor = position != 0 ? node_content.slice(position - 1, position) : '';
// Test for soft-hyphen
if (char_vor_cursor != '' && char_vor_cursor.charCodeAt(0) == 173) {
// correct innerHTML
var text_after_backspace = node_content.slice(0, position - 1) + '<span id="__ircaret" class="ircaret">\u2060</span>' + node_content.slice(position + 1);
node.innerHTML = text_after_backspace;
}
var caret_node = $(node).find('#__ircaret').get(0);
// select caretnode and remove
ed.selection.select(caret_node);
$(ed.getBody()).find('.ircaret').remove();
}
}

Auto-scroll <textarea> with Javascript

I have this textarea that shows inputted text, but when the number of lines exceeds the size and width of the textarea, the user has to scroll down to see what they have last inputted.
I'd like the textarea to be set to the bottom everytime the enter button is pressed.
I've tried the following, but I can't get it to work:
function inputKeyDown(evt, input) {
if (evt.keyCode == 13) {
var textarea = document.getElementById("textarea");
textarea.value += "\n" + ">" + " " + input.value;
input.value = "";
return false;
}
var elem = document.getElementById('textarea');
elem.scrollTop = elem.scrollHeight;
}
and then I call the function keyDown in <input onKeyDown="return keyDown(event, this);" ...>
Any idea why no workie?
Try the following:
textarea.scrollTop = textarea.scrollHeight - textarea.clientHeight;
It depends on what sort of input you are looking at, but forcing the cursor back to the bottom could potentially be a serious usability problem. If you need a textarea that automatically expands, have a look at existing solutions such as this jQuery plugin
I'm not really sure if this is what you want but have a look this: http://jsfiddle.net/yV76p/
var textarea = document.getElementById("textarea");
textarea.onkeyup = function(evt) {
this.scrollTop = this.scrollHeight;
}

Categories