Range text is same after deselect in contenteditable - javascript

I have a "mouseup" event handler inside a contenteditable to check wether text has been selected or not. If you highlight text after typing, all is good, but if you click again (to deselect text) the console shows that range.toString() is the same despite no text being selected.
http://jsfiddle.net/xpvt214o/857876/
type something in the contenteditable, then select it, then click again (to deselect) and check the console. Both console.logs will be the same.
I need to differentiate between a real selection and a deselection. If this is normal behavior, how can I tell the two events apart, or how can I do it differently to prevent this behavior?

Indeed, Chrome does change the active selection after the mouseup event.
You could add an hackish timeout to this event, which might break some times...
Or you could use the Selection API's selectionchange event.
This event will fire every time the selection does change:
$(document).on('selectionchange', function() {
console.log(getSelection().toString());
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div contenteditable="true" id="container">Select this dummy text</div>
Added benefit is that it will also work for keyboard selections.
And to get whether this event was a selection or an unselection, you can check if the content of the selection is empty, or if the current range is collapsed:
$(document).on('selectionchange', function() {
var isUnselect = getSelection().getRangeAt(0).collapsed;
viewer.style.backgroundColor = isUnselect ? 'red' : 'green';
})
#viewer{
width: 20px;
height: 20px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="viewer"></div>
<div contenteditable="true" id="container">Select this dummy text</div>

Please check if this helps.
$('#container').mouseup(function() {
console.log(getSelectedText());
});
function getSelectedText() {
if (window.getSelection) {
return window.getSelection().toString();
}
return '';
}

Related

Detect text-input elements in javascript

In JavaScript, I would like to detect some key presses. But I want to ignore those keypresses that the user makes while typing into some kind of text input area. How can I achieve that?
I have tried
if (document.activeElement.nodeName == 'INPUT') {
return;
}
if (document.activeElement.nodeName == 'TEXTAREA') {
return;
}
which works in those two elements; but for example, the email-compose element in Inbox by Gmail is a DIV container. Do I want to exclude all active DIVs, though? Are there other HTML elements that are used to obtain key input? Is there any much different method that I may have overlooked?
First of all, I'd use jquery to normalize your events. From there, try testing $(event.target).is():
Here is an example that has a div where all events are captured. Events for <input> elements are ignored, while others are not, including events for a <textarea>:
$('#mydiv').keypress(function(event) {
if ($(event.target).is('input')) {
return true;
} else {
console.log('not an input box');
return false;
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="mydiv">
<input>
<span>Spanning area</span>
<textarea></textarea>
</div>

How to stop my code putting tabs into my text boxes

I have this piece of jQuery to detect when the cursor is inside the text box. The idea is to highlight the table row that the text box appears is.
$(".text").on("focus", function() { //do something });
The problem is that this code seems to be registering the tab key inside the text box. The cursor will still move to the next text box when I hit the tab key. However it always insert a tab space into the box as well!!
This is most unexpected and I must admit i'm a little confused by it...
Any help on this matter would be brilliant, thank you.
It seems that the alert() you are sending in the focus event is interrupting things in a strange way. You can fix this by setting a brief timeout before sending the alert; that ensures that the alert is sent AFTER the text box receives focus and the tab input has been handled.
setTimeout(function() { alert("box selected"); }, 1);
http://jsfiddle.net/5cjbcy9o/2/
Give every row a tabindex like this
var i=2;
$('tr').each($(this).attr('tabindex',i++))
A previous answer has addressed listening to the tab key, by checking the keyCode to see if it matches 9. However, the width of a tab character differs (also reliant on personal preferences), although it is either two or four spaces commonly. Therefore, you can append that white space to the value of the input text when the tab keydown event is detected.
In the following code I have opted to use four white spaces:
$(function () {
$(".text").on("focus", function () {
console.log("box selected");
}).on("keydown", function(e) {
if ((e.keyCode || e.which) == 9) {
e.preventDefault();
$(this).val($(this).val() + " ");
}
});
});
See proof-of-concept fiddle here: http://jsfiddle.net/teddyrised/5cjbcy9o/1/

After clicking on selected text, window selection is not giving updated range

After double click, selection range can be obtained correctly on onclick event but when I again click on the selected text then updated selection range should be returned by window selection but this is not happening. Can anybody tell me if this is a bug in javascript selection or they have made it this way. And what could be the solution to get the updated range apart from timer.
<div id="xyz" contenteditable="true">Hello world</div>
<span class="status">Selected text : </span>
javascript code :
function yourFunction() {
if (window.getSelection) {
var selectionRange = window.getSelection();
$('.status').text(selectionRange.toString());
}
}
$('#xyz').click(function () {
$('.status').text('Mouse click');
yourFunction();
})
Example here
You fiddle is working just fine. But, yes sometimes when you do selections in quick succession, then it fails to register the click.
The problem really lies in the way you have implemented it on click on the text input itself. A click event is generated when a mouseup follows a mousedown. A selection happens when you mousedown then drag and then mouseup.
If you separate out the selection retrieval then this problem won't occur.
See this updated fiddle: http://jsfiddle.net/zRr4s/21/
Here, the selection retrieval is donw on a button click, instead of the input itself.
i.e., instead of:
$('#xyz').click(function (e) { ...
using this:
$('#btn').click(function () { ...
where, btn is:
<input id="btn" type="button" value="get selection" />
Hope that helps.
Update:
If you insist on handling event only on the input, then listening mouseup would be better option:
See this fiddle: http://jsfiddle.net/zRr4s/22/
$('#xyz').on("mouseup", function (e) { ...
Update 2:
To handle your requirement of in-context click, you will have to first clear the selection. For this to happen you will have to handle mousedown. So, that will defeat your purpose of having only one handler. Anyway,
You updated fiddle: http://jsfiddle.net/zRr4s/29/
And, this is how you do it:
$('#xyz').on("mousedown", function () {
clearTheSelection();
});
Where clearTheSelection is another function:
function clearTheSelection() {
if (window.getSelection) {
if (window.getSelection().empty) { // Chrome
window.getSelection().empty();
} else if (window.getSelection().removeAllRanges) { // Firefox
window.getSelection().removeAllRanges();
}
} else if (document.selection) { // IE?
document.selection.empty();
}
}
The complete code for the above function taken from this answer: https://stackoverflow.com/a/3169849/1355315
Hope that completes all your problems.
The fiddle provided in the question works fine in Edge and IE11 but doesn't work in Chrome. I found a trick to make it work everywhere. Add the following event handler in addition to the click handler you already have:
$(document).on("selectionchange", function () {
yourFunction();
});
Some notes:
selectionchange is a document level event, you cannot bind it to specific element (but you can find out whether you need to handle it within the event handler)
Handling selectionchange without also handling click doesn't work well in Edge and IE11
According to MDN, browser support is good enough: https://developer.mozilla.org/en-US/docs/Web/API/Document/selectionchange_event

Is there a JavaScript event that is triggered when a text selection is cleared?

I am using the JavaScript select event to detect when the user selects a range of characters within a textarea. However, the event does not seem to be invoked when the selection is cleared, either by clicking elsewhere in the textarea, or by moving the caret.
I have tested this in Chrome 12 and Firefox 4
There's onblur event which you can append after a selection has being made :)
var t = document.getElementsByTagName('textarea')[0];
t.onselect = function() {
alert('select');
if (typeof(t.onblur) !== 'function') {
t.onblur = function() {
alert('selection cleared');
t.onblur = null;
}
}
}
jsFiddle
My idea: create a function which will be executed after the text has been selected, and after it's being executed unset it ... since there's no text selection any more, and when the text has been selected again - the function will be defined again
How about a combination of click, blur, and keypress event bound to the element?
Here is a demo of JavaScript select/deselect event

Action on blur except when specific element clicked with jQuery

There are two elements in play:
$('#myInput') // an input field for search
$('#myList') // a list to display search results
I want to hide the list when the input no longer has focus, like so:
$('#myInput').blur(function() {
$('#myList').hide();
});
This works great, except when a list item is clicked, because the blur event fires and hides the list before the click is registered. The goal is for the list to stay visible when any part of the list is clicked, even though this will cause the input to blur.
How can I do this? Thanks!
You can accomplish this by keeping a global variable, and setTimouts, to wait a delay of 200ms and then check if one of the 2 elements have focus.
var keepFocus = false;
function hideList(){
if(!keepFocus){
$('#myList').hide();
}
}
$('#myInput').blur(function() {
keepFocus = false;
window.setTimeout(hideList, 200);
}).focus(function(){
keepFocus = true;
});
$('#myList').blur(function() {
keepFocus = false;
window.setTimeout(hideList, 200);
}).focus(function(){
keepFocus = true;
});
I've faced with the exact same problem, so this is how I solved it.
I came up with the fact that blur() fires earlier than click().
So I've tried to change click() to mousedown() and found out that mousedown() fires before blur().
And to imitate click() you'll have to fire mousedown() and then mouseup()
So in your case I would do something like this:
var click_in_process = false; // global
$('#myList').mousedown(function() {
click_in_process = true;
});
$('#myList').mouseup(function() {
click_in_process = false;
$('#myInput').focus();
// a code of $('#myList') clicking event
});
$('#myInput').blur(function() {
if(!click_in_process) {
$('#myList').hide();
// a code of what you want to happen after you really left $('#myInput')
}
});
Demo / example: http://jsfiddle.net/bbrh4/
Hope it helps!
You need to be able to say "do this blur() unless the list gains focus at the same time".
This question says how to detect if an element has focus: Using jQuery to test if an input has focus
Then all you need to do is:
$("#myInput").blur(function () {
if (!$("#myList").is(":focus")) {
$("#myList").hide();
}
});
Pigalev Pavel's answer above works great.
However, If you want an even simplier solution, you can just "prevent default" in the "mousedown" of an element to prevent the blur event from taking place. (since preventing default actually means that in the end, the input never looses focus in the first place!)
Of course, this is only if you're alright with preventing default in the div. It does have some side-effects, like the text is no longer selectable. As long as that's not an issue, this will work.
I suppose if you hold the mouse down over the div, move the mouse outside of the div, and then release the mouse, it also doesn't fire the "blur" event. But in my case, I wasn't too worried about that either, since the click started in the target div.
$("input").focus(function(){
$(this).val("");
});
$("input").blur(function(){
$(this).val("blur event fired!");
});
$("div").mousedown(function(e){
e.preventDefault();
})
div{
height: 200px;
width: 200px;
background: blue;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input>
<div>
Click here to prevent blur event!
</div>
The best way to do this is to attach an event handler to the body element, then another handler to the list that stops event propagation:
$(body).click(function () {
$("#myList").hide();
});
$("#myList").click(function (e) {
e.stopImmediatePropagation();
});
This listens for a click outside of #myInput and hides #myList. At the same time, the second function listens for a click on #myList and if it occurs, it prevents the hide() from firing.

Categories

Resources