Prevent text selection after double click - javascript

I'm handling the dblclick event on a span in my web app. A side effect of a double click is that it selects text on the page. How can I prevent this selection from happening?

function clearSelection() {
if(document.selection && document.selection.empty) {
document.selection.empty();
} else if(window.getSelection) {
var sel = window.getSelection();
sel.removeAllRanges();
}
}
You can also apply these styles to the span for all non-IE browsers and IE10:
span.no_selection {
user-select: none; /* standard syntax */
-webkit-user-select: none; /* webkit (safari, chrome) browsers */
-moz-user-select: none; /* mozilla browsers */
-khtml-user-select: none; /* webkit (konqueror) browsers */
-ms-user-select: none; /* IE10+ */
}

To prevent text selection ONLY after a double click:
You could use MouseEvent#detail property.
For mousedown or mouseup events, it is 1 plus the current click count.
document.addEventListener('mousedown', function(event) {
if (event.detail > 1) {
event.preventDefault();
// of course, you still do not know what you prevent here...
// You could also check event.ctrlKey/event.shiftKey/event.altKey
// to not prevent something useful.
}
}, false);
Some dummy text
See https://developer.mozilla.org/en-US/docs/Web/API/UIEvent/detail

In plain javascript:
element.addEventListener('mousedown', function(e){ e.preventDefault(); }, false);
Or with jQuery:
jQuery(element).mousedown(function(e){ e.preventDefault(); });

FWIW, I set user-select: none to the parent element of those child elements that I don't want somehow selected when double clicking anywhere on the parent element. And it works! Cool thing is contenteditable="true", text selection and etc. still works on the child elements!
So like:
<div style="user-select: none">
<p>haha</p>
<p>haha</p>
<p>haha</p>
<p>haha</p>
</div>

A simple Javascript function that makes the content inside a page-element unselectable:
function makeUnselectable(elem) {
if (typeof(elem) == 'string')
elem = document.getElementById(elem);
if (elem) {
elem.onselectstart = function() { return false; };
elem.style.MozUserSelect = "none";
elem.style.KhtmlUserSelect = "none";
elem.unselectable = "on";
}
}

For those looking for a solution for Angular 2+.
You can use the mousedown output of the table cell.
<td *ngFor="..."
(mousedown)="onMouseDown($event)"
(dblclick) ="onDblClick($event)">
...
</td>
And prevent if the detail > 1.
public onMouseDown(mouseEvent: MouseEvent) {
// prevent text selection for dbl clicks.
if (mouseEvent.detail > 1) mouseEvent.preventDefault();
}
public onDblClick(mouseEvent: MouseEvent) {
// todo: do what you really want to do ...
}
The dblclick output continues to work as expected.

If you are trying to completely prevent selecting text by any method as well as on a double click only, you can use the user-select: none css attribute. I have tested in Chrome 68, but according to https://caniuse.com/#search=user-select it should work in the other current normal user browsers.
Behaviorally, in Chrome 68 it is inherited by child elements, and did not allow selecting an element's contained text even if when text surrounding and including the element was selected.

or, on mozilla:
document.body.onselectstart = function() { return false; } // Or any html object
On IE,
document.body.onmousedown = function() { return false; } // valid for any html object as well

If you are using Vue JS, just append #mousedown.prevent="" to your element and it is magically going to disappear !

Old thread, but I came up with a solution that I believe is cleaner since it does not disable every even bound to the object, and only prevent random and unwanted text selections on the page. It is straightforward, and works well for me.
Here is an example; I want to prevent text-selection when I click several time on the object with the class "arrow-right":
$(".arrow-right").hover(function(){$('body').css({userSelect: "none"});}, function(){$('body').css({userSelect: "auto"});});
HTH !

To prevent IE 8 CTRL and SHIFT click text selection on individual element
var obj = document.createElement("DIV");
obj.onselectstart = function(){
return false;
}
To prevent text selection on document
window.onload = function(){
document.onselectstart = function(){
return false;
}
}

I know this is an old question but it is still perfectly valid in 2021. However, what I'm missing in terms of answers is any mentioning of Event.stopPropagation().
The OP is asking for the dblclick event but from what I see the same problem occurs with the pointerdown event. In my code I register a listener as follows:
this.addEventListener("pointerdown", this._pointerDownHandler.bind(this));
The listener code looks as follows:
_pointerDownHandler(event) {
// Stuff that needs to be done whenever a click occurs on the element
}
Clicking fast multiple times on my element gets interpreted by the browser as double click. Depending on where your element is located on the page that double click will select text because that is the given behavior.
You could disable that default action by invoking Event.preventDefault() in the listener which does solve the problem, at least in a way.
However, if you register a listener on an element and write the corresponding "handling" code you might as well swallow that event which is what Event.stopPropagation() ensures. Therefore, the handler would look as follows:
_pointerDownHandler(event) {
event.stopPropagation();
// Stuff that needs to be done whenever a click occurs on the element
}
Because the event has been consumed by my element, elements further up the hierarchy are not aware of that event and won't execute their handling code.
If you let the event bubble up, elements higher in the hierarchy would all execute their handling code but are be told to not do so by Event.preventDefault() which makes less sense to me than preventing the event from bubbling up in the first place.

Tailwind CSS:
<div class="select-none ...">
This text is not selectable
</div>

I had the same problem. I solved it by switching to <a> and add onclick="return false;" (so that clicking on it won't add a new entry to browser history).

Related

How to Access Dragged Text (Or: How Does Dragging Text into an Input "Work"?)

From what I've seen so far, we can use the onPaste event to validate/prevent content pasted into an <input> field. Likewise, if we want to validate/prevent a key press, we can use the onkeydown event. I'm curious about ondrag and ondrop.
Specifically, how can we retrieve the content that a user drags into a text <input>? If we wanted to grab the entire, updated input, we could just use the onchange or onblur events. However, I'm curious about grabbing just the dragged text -- similarly to how we can use event.which to grab just the pressed key.
Is the text data stored in the event somewhere for ondrag or ondrop -- and is it in a format that we can retrieve it?
I've been exploring the Dottoro docs (drag/drop) with no luck.
After some more snooping, I found a JavaScript example on Dottoro that led me down the rabbit hole.
Quick Answer
The text can be grabbed with event.dataTransfer.getData("Text") assuming that the browser supports dataTransfer objects. There are other restrictions as well -- such as a Webkit issue where getData is always empty on dragstart or dragover (source).
(Fiddle)
Likewise, the dragged text can be modified by using event.dataTransfer.setData("Text", newText). (Fiddle)
In both samples above, "Text" is the format of the dragged content we are retrieving/modifying. There are many options listed in the MDN documentation, but note that the available formats for a given "drag" can be found in the events.dataTransferTypes array.
Details and Context
The following code explains how to use the dataTransfer object and some peculiarities:
//Modify the text when some specific text is dragged.
function changeDraggedText(event) {
if (event.dataTransfer) {
// Note: textData is empty here for Safari and Google Chrome :(
var textData = event.dataTransfer.getData("Text");
var newText = "..." //Modify the data being dragged BEFORE it is dropped.
event.dataTransfer.setData (format, newText);
}
}
//Access the text when the `drag` ends.
function getDraggedText(event) {
if (event.dataTransfer) {
var format = "Text";
var textData = event.dataTransfer.getData (format);
if (!textData) {
// ... There is no text being dragged.
} else {
// ... Do what you will with the textData.
}
} else {
// ... Some (less modern) browsers don't support dataTransfer objects.
}
// Use stopPropagation and cancelBubble to prevent the browser
// from performing the default `drop` action for this element.
if (event.stopPropagation) {
event.stopPropagation ();
} else {
event.cancelBubble = true;
}
return false;
}
Which can just be bound to the ondrop and ondragstart events as in the following HTML:
<div ondragstart="changeDraggedText(event)">
Dragging these contents causes the `ondragstart` event to be fired.
</div>
<div ondragenter="return false;"
ondragover="return false;"
ondrop="getDraggedText(event);">
And likewise, the `ondrop` event gets fired if I drop anything in here.
</div>
Caution: if you don't override the ondragover and ondragenter events, they will treat drags as the browser normally treats them; this means you can't drop text onto a non-input block (such as a <div>).

Prevent text selection/highlighting in a content editable div

Is there way to create a content-editable div where users can't select/highlight content but can still input things? I want to create an interface where users are forced to delete and enter things key-by-key, without being able to make mass edits via highlighting.
I've looked into the various forms of the "user-select" property in CSS, which works for static content, but doesn't seem to work for content-editable elements/inputs.
Any ideas?
Thanks
If you can accept a textarea instead of a contenteditable div, you can do something like this:
window.onload = function () {
var div = document.getElementById('div');
if (div.attachEvent) {
div.attachEvent('onselectstart', function (e) {
e.returnValue = false;
return false;
});
div.attachEvent('onpaste', function (e) {
e.returnValue = false;
return false;
});
} else {
div.addEventListener('paste', function (e) {
e.preventDefault();
});
div.addEventListener('select', function (e) {
var start = this.selectionStart,
end = this.selectionEnd;
if (this.selectionDirection === 'forward') {
this.setSelectionRange(end, end);
} else {
this.setSelectionRange(start, start);
}
});
}
};
HTML:
<form>
<textarea id="div"></textarea>
</form>
A live demo at jsFiddle.
Some observations on the code:
In many browsers onselect is fired only for input or textarea elements within a form. That is a reason for the different HTML from yours.
IE9 - 10 don't support selectionDirection, that's why IE's legacy event handling model is used also for these browsers.
If not IE, you still can replace a bunch of text by selecting it with mouse and hitting a key without releasing the mouse button. I suppose this could be prevented by detecting if the mouse button is down, and in that case preventing keyboard actions. This would be your homework ; ).
The code for IE works with contenteditable divs too.
EDIT
Looks like I've done your "homework" too.
I know you want to disable mass deleting, but if others are wanting to not show a highlight color (which would be user-select: none for other elements), you can put the following in your css:
::selection { background: transparent }
This will make the selection look like user-select: none but it will still allow mass deleting. But it could make users think there is no selection, so they possibly won't mass delete.
Hope this works for you, but mainly towards others.

Is there a close event for the browser contextmenu

I'm catching the contextmenu event using jQuery like this:
$(document.body).on("contextmenu", function(e){
//do stuff here
});
So far, so good. Now I want to execute some code when it closes but I can't seem to find a correct solution for this.
Using something like the following would catch some of the cases, but not nearly all:
$(document.body).on("contextmenu click", function(e){});
It wouldn't be executed when:
the browser loses focus
an option in the contextmenu is chosen
the user clicks anywhere in the browser that's not on the page
note: I'm not using a jQuery context menu, I'm just using it to catch the event.
Following code may help you. jsfiddle
var isIntextMenuOpen ;
$(document).on("contextmenu", function(e){
isIntextMenuOpen = true;
});
function hideContextmenu(e){
if(isIntextMenuOpen ){
console.log("contextmenu closed ");
}
isIntextMenuOpen = false;
}
$(window).blur(hideContextmenu);
$(document).click(hideContextmenu);
I needed to detect when a context menu closes and so I came up with a solution.
Fiddle: https://jsfiddle.net/kexp0nmd/1/
var premenuelem;
var TempContextMenuCloseHandler = function(e) {
console.log('closed!');
//console.log(e);
window.removeEventListener('keyup', TempContextMenuCloseHandler, true);
window.removeEventListener('mousedown', TempContextMenuCloseHandler, true);
window.removeEventListener('focus', TempContextMenuCloseHandler, true);
var focuselem = document.getElementById('tempfocus');
if (focuselem === document.activeElement) premenuelem.focus();
focuselem.style.display = 'none';
};
var TempContextMenuHandler = function(e) {
console.log('open!');
//console.log(e);
premenuelem = document.activeElement;
var focuselem = document.getElementById('tempfocus');
focuselem.style.display = 'block';
focuselem.focus();
window.addEventListener('keyup', TempContextMenuCloseHandler, true);
window.addEventListener('mousedown', TempContextMenuCloseHandler, true);
window.addEventListener('focus', TempContextMenuCloseHandler, true);
};
window.addEventListener('contextmenu', TempContextMenuHandler, true);
html, body { min-height: 100%; }
<textarea></textarea>
<div id="tempfocus" tabIndex="-1" style="left: 0; bottom: 0; height: 50px; width: 100%; background-color: #CCCCCC; display: none; position: fixed; outline: none;"></div>
Tested and verified working as of May 2020 on Edge, Firefox 76, and Chrome 80 for both mouse and keyboard. Mobile/touch support unknown.
The key aspect of this solution is using an element that has a tabIndex on it. By showing and moving focus to that element (focus stealing) before the context menu appears causes Edge and Chrome to send a focus change event when the user later closes the context menu. I made the background of the div gray so it could be seen - in production, make it a transparent background and style it up however you want.
The keyup handler catches the release of the Escape/Enter key for when the keyboard closes the context menu. The mousedown handler catches mousedown events in Firefox only.
As far as I can tell, there is no way to know for certain what option a user selected or even if they did, in fact, select an option. At the very least, it allows for consistent detection of context menu open/close across all major browsers.
The textarea in the example is just there to give something else to play with for focus handling.
While this solution involves temporary focus stealing, it is the cleanest, cross-browser solution until browser vendors and the W3C add an 'exitcontextmenu' event or some such to the DOM.
One minor bug I just ran into: Showing the context menu and switching away to another application closes the context menu but does not fire the closed event right away. However, upon switching back to the web browser, the event fires and the close handler runs. Adding a 'blur' capture to the window might solve that but then I'd have to re-test everything and it might break something (e.g. fire blur on opening the context menu). Not worth fixing for the extremely rare occasion this might happen AND the handler still fires - it's just visibly delayed.

Disabling tab focus on form elements

I have several divs within the same form. What I am trying to do is to disable the Tab key in one of the divs in the form without disabling the tab in the other divs in the same form.
Example Form:
div1 - disable Tab
div2 - Tab works
div3 - Tab works
A simple way is to put tabindex="-1" in the field(s) you don't want to be tabbed to.
Eg
<input type="text" tabindex="-1" name="f1">
Similar to Yipio, I added notab="notab" as an attribute to any element I wanted to disable the tab too. My jQuery is then one line.
$('input[notab=notab]').on('keydown', function(e){ if (e.keyCode == 9) e.preventDefault() });
Btw, keypress doesn't work for many control keys.
Building on Terry's simple answer I made this into a basic jQuery function
$.prototype.disableTab = function() {
this.each(function() {
$(this).attr('tabindex', '-1');
});
};
$('.unfocusable-element, .another-unfocusable-element').disableTab();
My case may not be typical but what I wanted to do was to have certain columns in a TABLE completely "inert": impossible to tab into them, and impossible to select anything in them. I had found class "unselectable" from other SO answers:
.unselectable {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
This actually prevents the user using the mouse to put the focus in the TD ... but I couldn't find a way on SO to prevent tabbing into cells. The TDs in my TABLE actually each has a DIV as their sole child, and using console.log I found that in fact the DIVs would get the focus (without the focus first being obtained by the TDs).
My solution involves keeping track of the "previously focused" element (anywhere on the page):
window.currFocus = document;
//Catch any bubbling focusin events (focus does not bubble)
$(window).on('focusin', function () {
window.prevFocus = window.currFocus;
window.currFocus = document.activeElement;
});
I can't really see how you'd get by without a mechanism of this kind... jolly useful for all sorts of purposes ... and of course it'd be simple to transform it into a stack of recently focused elements, if you wanted that...
The simplest answer is then just to do this (to the sole DIV child in every newly created TD):
...
jqNewCellDiv[ 0 ].classList.add( 'unselectable' );
jqNewCellDiv.focus( function() {
window.prevFocus.focus();
});
So far so good. It should be clear that this would work if you just have a TD (with no DIV child).
Slight issue: this just stops tabbing dead in its tracks. Clearly if the table has any more cells on that row or rows below the most obvious action you'd want is to making tabbing tab to the next non-unselectable cell ... either on the same row or, if there are other rows, on the row below. If it's the very end of the table it gets a bit more tricky: i.e. where should tabbing go then. But all good clean fun.
You have to disable or enable the individual elements. This is how I did it:
$(':input').keydown(function(e){
var allowTab = true;
var id = $(this).attr('name');
// insert your form fields here -- (:'') is required after
var inputArr = {username:'', email:'', password:'', address:''}
// allow or disable the fields in inputArr by changing true / false
if(id in inputArr) allowTab = false;
if(e.keyCode==9 && allowTab==false) e.preventDefault();
});
If you're dealing with an input element, I found it useful to set the pointer focus to back itself.
$('input').on('keydown', function(e) {
if (e.keyCode == 9) {
$(this).focus();
e.preventDefault();
}
});
$('.tabDisable').on('keydown', function(e)
{
if (e.keyCode == 9)
{
e.preventDefault();
}
});
Put .tabDisable to all tab disable DIVs
Like
<div class='tabDisable'>First Div</div> <!-- Tab Disable Div -->
<div >Second Div</div> <!-- No Tab Disable Div -->
<div class='tabDisable'>Third Div</div> <!-- Tab Disable Div -->

Any way to prevent "deselection" of highlighted text?

Highlight some text on this webpage, then click basically anywhere on the document. Your selection will disappear.
Is there a way to prevent this behavior when the user clicks on a specific element, either by CSS or Javascript?
E.g.:
var element = document.getElementById("foo");
foo.onclick = function(e){
//some magic here that prevents deselection from occuring
}
or
foo.style.preventDeselect = "true";
Edit: Perhaps I could store the selection, then after "mouseclick" restore the selection? Is there a way to store aselection, and then reselect it?
Thanks!
"return false" as well as "e.preventDefault()" in onmousedown works in FF and Safari, but not IE. The only solution for IE, as far as I can tell, is to throw an error.
This works in all browsers (but causes an error in IE, since preventDefault is not a method):
//clicking the 'test' element will not deselect text.
var test = document.getElementById("test");
test.onmousedown = function(e){
e = e || window.event;
e.preventDefault();
}
I'd still like to do this error-free in IE, if possible
Thanks to Paolo Bergantino for the the "onmousedown" tip.
This works for me on Firefox, haven't tried IE though. Try clicking on the foo.style.preventDeselect = "true"; line when you have text selected. Uses the mousedown event.
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="http://code.jquery.com/jquery-latest.js"></script>
</head>
<script>
$(document).ready(function() {
$('#test').mousedown(function() {
return false;
});
});
</script>
<body>
Highlight some text on this webpage, then click basically anywhere on the document. Your selection will disappear.<br><Br>
Is there a way to prevent this behavior when the user clicks on a specific element, either by CSS or Javascript?<br><Br>
E.g.:<br><Br>
var element = document.getElementById("foo");<br>
foo.onclick = function(e){<br>
//some magic here that prevents deselection from occuring<br>
}<br><Br>
or<br><Br>
<span id='test'>foo.style.preventDeselect = "true";</span><br><Br>
Thanks!<br><Br>
</body>
</html>
This behavior, while mostly universal in modern browsers, is browser/implementation specific and almost completely unrelated to CSS or Javascript. In particular, note that Firefox maintains separate selection states for the page at large and the contents of text boxes, while IE does not do this. Even worse, consider text-mode browsers with separate mouse and keyboard selection interfaces.
What about
if(e.preventDefault)
e.preventDefault();
else
e.returnValue = false;
Just in case somebody checks this out, I had a similar problem. I needed to run some javascript on a piece of selected text that was fired by clicking on a toolbar button (span with background-image). I solved my problem by changing the spans to anchors with an href of "javascript:void(0)". That prevented my selected text from being deselected.
the best way that I figured out how to do this is, for IE you need to setup a listener on 'onselectstart'. For Mozilla and other browsers you can do on 'mousedown'. This will only work, if you don't use 'mousedown' in any other portion of your site!
Snippet:
YUI way:
FF browser:
YAHOO.util.Event.addListener(window,
'mousedown', function(evt) {
YAHOO.util.Event.preventDefault(evt);
});
IE Browser:
In IE you are not allowed to set this listener on window, since in IE it doesn't work.
best thing to do is on your body element set a class element, then reference it, and apply
the listener to that element object.
// Get the element by class and
assign to var bar
YAHOO.util.Event.addListener(bar,
'selectstart', function(evt) {
YAHOO.util.Event.preventDefault(evt);
});
if you want to do it the easy way, just do
window.onMouseDown = function()
{return false;}
or for IE
body.onSelectStart = function()
{return false;}
Hope this helps.

Categories