Prevent text selection/highlighting in a content editable div - javascript

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.

Related

Qualtrics Javascript focus / select / place cursor on text box when page loads

I am trying to make Qualtrics focus automatically on a text box (single question per page) so participants can start typing right away.
I have tried different things from other answers (e.g.,Qualtrics: Automatic blinking cursor (focus) does not work on JFE, only on SE surveybuilder) but the focusing doesn't work on browsers like IE (Firefox, without including code, automatically focuses on the question).
The code also seems to invalidate code for advancing to next page by pressing "Enter" (below), so the survey gets stuck on the page
Qualtrics.SurveyEngine.addOnload(function()
{
this.hideNextButton();
this.hidePreviousButton();
var that = this;
Event.observe(document, 'keydown', function keydownCallback(e) {
var choiceID = null;
switch (e.keyCode) {
case 13: // 'enter' was pressed
choiceID = 1;
break;
}
if (choiceID) {
Event.stopObserving(document, 'keydown', keydownCallback);
that.clickNextButton();
}
});
});
Any ideas how to fix this? thank you!
The problem with your code may be hideNextButton with addOnload due to timing issues. Use addOnReady instead. Also, it is best to check for the existence of the PreviousButton first. If is is not there it will cause an error and stop your script. On some browsers you have to select a field before you can focus on it, activate() does that. Finally, it is better to add your event handler to the text field, instead of the document.
Try this:
Qualtrics.SurveyEngine.addOnReady(function()
{
$('NextButton').hide();
if($('PreviousButton')) $('PreviousButton').hide();
var inputText = $(this.questionId).down('.InputText');
var evt = inputText.on('keydown', function(e) {
if(e.which == 13) {
evt.stop();
$('NextButton').click();
}
});
inputText.activate();
});
Is there any way to have the cursor placed in the first text box, say for a form field in Qualtrics? I need people to input their contact information (street address, city, state etc.) and I'd like the cursor to be automatically placed in the top box. I can do it with single questions but haven't figured out how to do it with the form field.
Thanks!

How to disable tab key globally except for all forms in a page with JavaScript?

I'm making a single page app that is launching next week, for a pretty huge client, and going live for a pretty big event and well, there's still a ton to finish before then.
There's 100+ 'pages' which are all loaded within a single 700px x 600px window, and I had learned not long ago you could tab through the page/sections, which in-turn would break the app because it would bring focus to hidden off-screen elements, so for this reason, I disabled the tab key for the entire app.
But now there are a couple places where we have a form with a handful of input fields which you are not able to tab through as you fill in the form. It's a pain in the ass.
I need to make it so you can tab through the form fields, but only the form fields. I have the tabindex attribute set for the form, and have tried to make inputs tab enabled but was not able to make it work without causing the app to jump to hidden sections.
Here's the function I need to change so it will disable tab key except from input to input fields in a form.
window.onkeydown = function(e) {
if (e.keyCode === tab) {
return false;
}
}
I tried to do this, which obv didnt work lol
$('input').keydown(function(e) {
if (e.keyCode === tab) {
return true;
}
});
Thanks :)
I made some fixes to what #Joseph posted for an answer to this that handle being able to shift + tab through inputs of a form so you can reverse direction. It was a very annoying thing for me before when I first had to find a way to do this, and didn't have time to waste anymore trying to find a complete solution for this until now. Here it is.
$(function() {
// gather all inputs of selected types
var inputs = $('input, textarea, select, button'), inputTo;
// bind on keydown
inputs.on('keydown', function(e) {
// if we pressed the tab
if (e.keyCode == 9 || e.which == 9) {
// prevent default tab action
e.preventDefault();
if (e.shiftKey) {
// get previous input based on the current input
inputTo = inputs.get(inputs.index(this) - 1);
} else {
// get next input based on the current input
inputTo = inputs.get(inputs.index(this) + 1);
}
// move focus to inputTo, otherwise focus first input
if (inputTo) {
inputTo.focus();
} else {
inputs[0].focus();
}
}
});
});
Demo of it working http://jsfiddle.net/jaredwilli/JdJPs/
Have you tried setting tabIndex="-1" on all elements that you don't want to be able to tab to? I think that's a much better solution.
Otherwise, within your key handler function test event.target (or event.srcElement in IE) to see if the event originated with a form element. You seem to be using jQuery, so you could assign an "allowTab" class just to the fields in your form and then do this:
$(document).keydown(function(e) {
if (!$(e.target).hasClass("allowTab"))
return false;
});
Or
if (e.target.tagName !== "input")
// etc
what we do is to determine what input is next in line and skip to it!:
http://jsfiddle.net/qXDvd/
$(document).ready(function() {
//gather all inputs of selected types
var inputs = $('input, textarea, select, button');
//bind on keydown
inputs.on('keydown', function(e) {
//if we pressed the tab
if (e.keyCode == 9 || e.which == 9) {
//prevent default tab action
e.preventDefault();
//get next input based on the current input we left off
var nextInput = inputs.get(inputs.index(this) + 1);
//if we have a next input, go to it. or go back
if (nextInput) {
nextInput.focus();
}
else{
inputs[0].focus();
}
}
});
});​
may need some optimization but it works. this was originally meant to skip non-form elements. you can add selectors not to skip if you like. additionally, you can add logic for the Shift+Tab behavior (maybe before the tab logic)
obviously, it will still go through some elements according to how they appear in the source. however, why not just remove those hidden elements from the DOM but still keep track of them using the methods found in this question. that way, you won't have the pain of having to cycle back and forth through off-screen elements.

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 -->

Google Chrome: Focus issue with the scrollbar

I am using jQuery 1.3.2.
There is an input field in a form.
Clicking on the input field opens a div as a dropdown. The div contains a list of items. As the list size is large there is a vertical scrollbar in the div.
To close the dropdown when clicked outside, there is a blur event on the input field.
Now the problem is:
In chrome(2.0.172) when we click on the scrollbar, the input field will loose focus.
And now if you click outside, then the dropdown won't close(as the input has already lost focus when you clicked on the srollbar)
In Firefox(3.5), IE(8), opera(9.64), safari() when we click on the scrollbar the input field will not loose focus. Hence when you click outside (after clicking on the srollbar) the dropdown will close. This is the expected behaviour.
So In chrome once the scrollbar is clicked, and then if I click outside the dropdown won't close.
How can i fix this issue with chrome.
Well, I had the same problem in my dropdown control. I've asked Chrome developers concerning this issue, they said it's a bug that is not going to be fixed in the nearest future because of "it has not been reported by many people and the fix is not trivial". So, let's face the truth: this bug will stay for another year at least.
Though, for this particular case (dropdown) there is a workaround. The trick is: when one click on a scrollbar the "mouse down" event comes to the owner element of that scrollbar. We can use this fact to set a flag and check it in "onblur" handler. Here the explanation:
<input id="search_ctrl">
<div id="dropdown_wrap" style="overflow:auto;max-height:30px">
<div id="dropdown_rows">
<span>row 1</span>
<span>row 2</span>
<span>row 2</span>
</div>
</div>
"dropdown_wrap" div will get a vertical scrollbar since its content doesn't fit fixed height. Once we get the click we are pretty sure that scrollbar was clicked and focus is going to be taken off. Now some code how to handle this:
search_ctrl.onfocus = function() {
search_has_focus = true
}
search_ctrl.onblur = function() {
search_has_focus = false
if (!keep_focus) {
// hide dropdown
} else {
keep_focus = false;
search_ctrl.focus();
}
}
dropdow_wrap.onclick = function() {
if (isChrome()) {
keep_focus = search_has_focus;
}
}
That's it. We don't need any hacks for FF so there is a check for browser. In Chrome we detect click on scrollbar, allow bluring focus without closing the list and then immediately restore focus back to input control. Of course, if we have some logic for "search_ctrl.onfocus" it should be modified as well. Note that we need to check if search_ctrl had focus to prevent troubles with double clicks.
You may guess that better idea could be canceling onblur event but this won't work in Chrome. Not sure if this is bug or feature.
P.S. "dropdown_wrap" should not have any paddings or borders, otherwise user could click in this areas and we'll treat this as a scrollbar click.
I couldn't get these answers to work, maybe because they are from 2009. I just dealt with this, I think ihsoft is on the right track but a bit heavy handed.
With two functions
onMouseDown() {
lastClickWasDropdown=true;
}
onBlur() {
if (lastClickWasDropdown) {
lastClickWasDropdown = false;
box.focus();
} else {
box.close();
}
}
The trick is in how you bind the elements. The onMouseDown event should be on the "container" div which contains everything that will be clicked (ie, the text box, the dropdown arrow, and the dropdown box and its scroll bar). The Blur event (or in jQuery the focusout event) should be bound directly to the textbox.
Tested and works!
I was facing the same situation/problem and I tested the solution from "ihsoft" but it has some issues. So I worked on an alternative for that and made just one similar to "ihsoft" but one that works. here is my solution:
var hide_dropdownlist=true;
search_ctrl.onblur = function() {
search_has_focus = false
if (hide_dropdownlist) {
// hide dropdown
} else {
hide_dropdownlist = true;
search_ctrl.focus();
}
}
dropdow_wrap.onmouseover = function() {
hide_dropdownlist=false;
}
dropdow_wrap.onmouseoout = function() {
hide_dropdownlist=true;
}
I hope this will help someone.
Earlier also I faced such situation and this is what I have been doing.
$('html').click(function() {
hasFocus = 0;
hideResults();
});
and on the input field i will do this
$('input').click()
{
event.stopPropagation();
}
So this will close the drop down if clicked anywhere outside the div (even the scrollbar).
But I thought if someone could provide a more logical solution.
Could you maybe set the blur event to fire on the drop down div as well? This way, when either the input or the drop down loses focus, it will dissapear...
I'm curious...
You're using the last version of every browser, why don't you try it in chrome 4.0.202?
instead of detecting the blur, detect the document.body or window click and grab the mouse point. determine if this mouse point is outside of the menu box. presto, you've detected when they clicked outside the box!
I solved this by doing the following:
#my_container is the container which has the "overflow: auto" CSS rule
$('#my_container')
.mouseenter(function(){
// alert('ctr in!');
mouse_in_container = true;
})
.mouseleave(function(){
// alert('ctr out!');
mouse_in_container = false;
});
And then:
$('input').blur(function(){
if(mouse_in_container)
return;
... Normal code for blur event ...
});
When I select an element in the drop down, I rewrite the code as:
(>> ADDED THIS) mouse_in_container=false;
$('input').attr('active', false); // to blur input
$('#my_container').hide();

Prevent text selection after double click

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).

Categories