Forum Moderators: open

Message Too Old, No Replies

Prevent tab text highlight / disable tab text selection

         

Rain_Lover

7:06 pm on Apr 21, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Hi,

Here's a sample form:

<form action="#" method="post">
Name:<br />
<input type="text" name="name" value="your name" /><br />
E-mail:<br />
<input type="text" name="mail" value="your email" /><br />
<input type="submit" value="Send">
</form>


When you tab to a text input, the value gets highlighted. How can it be disabled?

Any help is appreciated!
Rain Lover

lucy24

9:07 pm on Apr 21, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Is this really a java question? It sounds like something that's determined by the user's browser preferences, suggesting that the answer lies in CSS.

:: obligatory boilerplate ::

Do you have a strong reason for wanting to override this preference for everyone? If users expect a form to behave a certain way, it may be counterproductive to make it behave differently. "Did my answer get recorded? How come it isn't highlighted? What did I do wrong?"

Rain_Lover

8:11 am on Apr 22, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Do you have a strong reason for wanting to override this preference for everyone?


When you focus on a text field using your mouse, the cursor automatically goes to the end of the text value. I want the same behavior on a focus by keyboard -- not highlighting the text.

astupidname

9:22 am on Apr 22, 2011 (gmt 0)

10+ Year Member



Though I agree entirely with lucy24, and will also emphasize that the users web experience is not about what YOU want, I'll still sell you the gun -what you do with it is up to you:


<script type="text/javascript">

//elem must be an element from the DOM as an object
//selStart and selEnd must be integers, where to start and end the selection
function setSelectedText(elem, selStart, selEnd) {
if (elem.setSelectionRange) { //standard
elem.focus();
elem.setSelectionRange(selStart, selEnd);
} else if (elem.createTextRange) { //IE
var range = elem.createTextRange();
range.collapse(true);
range.moveEnd('character', selEnd);
range.moveStart('character', selStart);
range.select();
}
}

document.onkeyup = function (evt) {
var evt = evt || window.event, //window.event for IE
el = evt.target || evt.srcElement, //srcElement for IE
nn = el.nodeName.toLowerCase(),
kc = evt.which || evt.keyCode; //keyCode for IE
if (kc == 9/*tab key*/ && nn == 'input' && el.type.toLowerCase() == 'text') {
//an input of type text has been tabbed into,
//set it's text selection to the end of it:
setSelectedText(el, el.value.length, el.value.length);
}
};

</script>

<form action="#" method="post">
Name:<br />
<input type="TEXT" name="name" value="your name" /><br />
E-mail:<br />
<input type="text" name="mail" value="your email" /><br />
<input type="submit" value="Send">
</form>

If anybody's wondering, how to post formatted code on webmasterworld [webmasterworld.com]
Do not copy formatted code on webmasterworld from IE, use other browser such as Firefox.

Rain_Lover

9:27 am on Apr 22, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Perfect!
Just a tiny point: before I see the cursor at the desired place, the text get selected for a short while. Can it be avoided?

astupidname

9:46 am on Apr 22, 2011 (gmt 0)

10+ Year Member



No, unfortunately not. I tried a number of things, different methods of going about it (knowing you were going to ask that :), and was not able to avoid that any way I go about it. One reason being that to determine the tabbed-into element required using onkeyup rather than onkeydown which would have returned the wrong element (previously focused element), and I don't know of/or if there even is/ any way to determine the 'next-tabbed-into' element before it gets focus. So the element has to receive focus first to determine it is of the type we want to affect.
As you say though, a tiny point.
Good luck!

Rain_Lover

9:49 am on Apr 22, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Thanks so much! :)

Fotiman

1:12 pm on Apr 22, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Here's an approach that does not have the delay. It works by attaching to the onfocus of the text input instead. Note, I had to call setTimeout to schedule the setSelectionRange so that it happens AFTER the browser default action, otherwise it would still appear to be selected because after you set it in the handler, the browser default action would still run and undo your work.

<!DOCTYPE html>
<html>
<head>
<title>Change Tab Selection Test</title>
</head>
<body>
<p>
Tab to the 2nd input below and notice that the cursor is at the end of
the text box with nothing selected.
</p>
<input id="foo" value="Hello">
<input id="bar" value="World">
<script>
window.onload = function () {
var bar = document.getElementById('bar');
bar.onfocus = function () {
var idx = bar.value.length;
if (bar.setSelectionRange) {
setTimeout(function () {bar.setSelectionRange(idx, idx);},0);
}
else if (bar.createTextRange) {
var range = bar.createTextRange();
range.collapse(true);
range.moveEnd('character', idx);
range.moveStart('character', idx);
range.select();
}
}
}
</script>
</body>
</html>

Rain_Lover

1:40 pm on Apr 22, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



It works like a charm as expected. But how can I include your code in my real form:

<!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>
<script type="text/javascript" src="http://dev.jquery.com/view/trunk/plugins/validate/jquery.validate.js"></script>
<style type="text/css">
* { font-family: Verdana; font-size: 96%; }
label { width: 10em; float: left; }
label.error { float: none; color: red; padding-left: .5em; vertical-align: top; }
p { clear: both; }
.submit { margin-left: 12em; }
em { font-weight: bold; padding-right: 1em; vertical-align: top; }
</style>
<script>
$(document).ready(function(){
$("#curl").focus(function(){
if( this.value == this.defaultValue ) {
$(this).val("http://");
}
});
$("#commentForm").validate();
});
</script>

</head>
<body>


<form class="cmxform" id="commentForm" method="get" action="">
<fieldset>
<p>
<input id="cname" name="name" size="25" class="required" value="Name" />
</p>
<p>
<input id="cemail" name="email" size="25" class="required email" value="Email" />
</p>
<p>
<input id="curl" name="url" size="25" class="url" value="URL" />
</p>
<p>
<textarea id="ccomment" name="comment" cols="22" rows="5" class="required">Comment</textarea>
</p>
<p>
<input class="submit" type="submit" value="Submit"/>
</p>
</fieldset>
</form>
</body>
</html>

Fotiman

9:12 pm on Apr 22, 2011 (gmt 0)

WebmasterWorld Senior Member 10+ Year Member Top Contributors Of The Month



Within your ready callback function, you could add the following:

function moveCursorToEnd(el) {
var idx = el.value.length;
if (el.setSelectionRange) {
setTimeout(function () {el.setSelectionRange(idx, idx);},0);
}
else if (el.createTextRange) {
var range = el.createTextRange();
range.collapse(true);
range.moveEnd('character', idx);
range.moveStart('character', idx);
range.select();
}
}


And then for each input that you wanted to apply this to:

$('#cemail').focus(function () {
moveCursorToEnd(this);
});


Or whatever selector(s) you wanted in order to apply it to the items that you want it applied to.

Rain_Lover

8:18 am on Apr 23, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Just what I wanted! Thanks!

astupidname

12:06 pm on Apr 23, 2011 (gmt 0)

10+ Year Member



I tried a number of things, different methods of going about it


Yeah, something quite similar to Fotiman's solution is one of the things I had tried, involving the inputs onfocus and window.setTimeout. Don't remember exactly why I abandoned that initially... but I think it is because the event that an input receives to trigger it's onfocus event handlers/listeners does not contain a keyCode. Meaning that from within the input's onfocus you can not directly determine whether it was focused via the tab key or not.
I noticed this flaw in Fotiman's otherwise fine solution, that his solution does not watch for tabbing at all, but rather picks up any focusing of the input element. This is another perversion of usability which to me is not acceptable. If the input has the word "World" in it and a user places his cursor between the 'r' and the 'l' and clicks to place his focus there, the focus inadvertantly skips to the end of the inputs value instead! Unacceptable behavior when the user has explicitly requested an action and received something bizarre like that.

While I was re-visiting this and attempting to re-think it, I had the idea also that I could develop a tabIndex manager which would grab control of the tabindex order of things in the document, track tabbing and so on... but here firefox throws a wrench in to things that would need fixing and I also fear such a solution would blow usability as well by not being able to allow tabbing out to the browser interface outside of the document.

So, I decided to revisit the onfocus approach, actually a bit inspired by Fotiman's near success :) The key for me turned out to be that you need to watch events on the document, and then in the input's onfocus event handler or listener check the last document event for whether it was a keydown or keypress(firefox grrr...) event and then check if the keyCode was 9 (tab key). If so, then do a setTimeout to set the selection point at end of inputs value, and exit the onfocus function first returning false. My revised script is below. Implementation is a cinch, as it will automatically apply the affect to all input elements in the document which are of type 'text' (or no type defined, defaulting to 'text') at time of window load. Simply place the below script into an external file and link to it with script tags and you're good to go, in a slightly less obtrusive manner.

var CURRENT_EVENT; //global var will be used to track and easily access latest document event

//elem must be an element from the DOM as an object
//selStart and selEnd must be integers, where to start and end the selection
function setSelectedText(elem, selStart, selEnd) {
if (elem.setSelectionRange) { //standard
elem.focus();
elem.setSelectionRange(selStart, selEnd);
} else if (elem.createTextRange) { //IE
var range = elem.createTextRange();
range.collapse(true);
range.moveEnd('character', selEnd);
range.moveStart('character', selStart);
range.select();
}
}

//el must be an element from the DOM as an object
//type should be string name of event type ('on' prefix not required, so 'mouseover' or 'onmouseover' works fine)
//fn should be a function to set as an event handler
function addThisEvent(el, type, fn) {
var i, len, ename = type.replace(/^on/i, '');
if (el.attachEvent) { //IE
el.attachEvent('on'+ ename, function () {
return fn.call(el, window.event); //fixes IE's fudging of 'this' in attachEvent
});
} else if (el.addEventListener) { //Standard
el.addEventListener(ename, fn, false );
} else {
el["on"+ename] = fn;
}
}

addThisEvent(window, 'load', function () {
var inputs = document.getElementsByTagName('input');
for (var i = 0, len = inputs.length; i < len; i++) {
if (inputs[i].type == 'text') {
addThisEvent(inputs[i], 'focus', function () {
var O = this,
len = O.value.length,
//firefox is a big p.o.s. these days... it is keypress & others are keydown
lastWasKey = /key(press|down)/i.test(CURRENT_EVENT.type);
if (lastWasKey && CURRENT_EVENT.keyCode == 9/*tab key*/) {
window.setTimeout(function () {
setSelectedText(O, len, len);
}, 0);
return false;
}
});
}
}
});

var eventTypes = ['click', 'dblclick', 'mousedown', 'mousemove', 'mouseout', 'mouseover', 'mouseup', 'keydown', 'keypress', 'keyup'];
for (var i = 0, len = eventTypes.length; i < len; i++) {
addThisEvent(document, eventTypes[i], function (evt) {
var evt = evt || window.event, //window.event for IE
el = evt.target || evt.srcElement, //srcElement for IE
kc = evt.which || evt.keyCode, //keyCode for IE
cc = evt.charCode || evt.keyCode; //cc for getting charCode, IE uses keyCode onkeypress
if (el.nodeType == 3) { //safari needs
el = el.parentNode;
}
CURRENT_EVENT = {
event : evt,
target : el,
keyCode : kc,
charCode : cc,
type : evt.type
};
});
}

If anybody's wondering, how to post formatted code on webmasterworld [webmasterworld.com]
Do not copy formatted code on webmasterworld from IE, use other browser such as Firefox.

Rain_Lover

3:52 pm on Apr 23, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Dear astupidname,

You're knowledgeable and I really appreciate your time and attempt to improve the offered solutions. However, I just included your script in my real form and tried it in IE6 and it didn't work well. On a tab focus the cursor goes to the middle of the text:
[rain-lover.webs.com ]

astupidname

11:56 pm on Apr 23, 2011 (gmt 0)

10+ Year Member



Hi again, sorry I had not actually tested in your latest form with the jquery stuff, rather your previous form example. I no longer have IE 6 to test in and do not bother to support it anymore. But I do see what you are talking about in IE 7 & 8 but only in the url field because you have the script affecting that fields value. Are you saying you have the issue in other fields as well, or just the url field? At any rate, I'm now realizing that at times IE needs to check for the presence of CURRENT_EVENT first so I've added an 'if' statement for that, plus we can alter the duration of the setTimeout slightly to give your other code an instant to do it's effect first.
Change:
addThisEvent(window, 'load', function () {
var inputs = document.getElementsByTagName('input');
for (var i = 0, len = inputs.length; i < len; i++) {
if (inputs[i].type == 'text') {
addThisEvent(inputs[i], 'focus', function () {
var O = this,
len = O.value.length,
//firefox is a big p.o.s. these days... it is keypress & others are keydown
lastWasKey = /key(press|down)/i.test(CURRENT_EVENT.type);
if (lastWasKey && CURRENT_EVENT.keyCode == 9/*tab key*/) {
window.setTimeout(function () {
setSelectedText(O, len, len);
}, 0);
return false;
}
});
}
}
});


TO:
addThisEvent(window, 'load', function () {
var inputs = document.getElementsByTagName('input');
for (var i = 0, len = inputs.length; i < len; i++) {
if (inputs[i].type == 'text') {
addThisEvent(inputs[i], 'focus', function () {
if (CURRENT_EVENT) {//IE needs the 'if' at times...
var O = this,
len = O.value.length,
//firefox is a big p.o.s. these days... it is keypress & others are keydown
lastWasKey = /key(press|down)/i.test(CURRENT_EVENT.type);
if (lastWasKey && CURRENT_EVENT.keyCode == 9/*tab key*/) {
window.setTimeout(function () {
setSelectedText(O, len, len);
}, 1); //<--adjust the timeout duration as needed>
return false;
}
}
});
}
}
});

astupidname

1:01 am on Apr 24, 2011 (gmt 0)

10+ Year Member



Oops.. I think IE tricked me somehow there, that does not work either... although the 'if' statement is needed in some instances.
Change that latest bit of script I posted to this instead:

addThisEvent(window, 'load', function () {
var inputs = document.getElementsByTagName('input');
for (var i = 0, len = inputs.length; i < len; i++) {
if (inputs[i].type == 'text') {
addThisEvent(inputs[i], 'focus', function () {
if (CURRENT_EVENT) {//IE needs the 'if' at times...
var O = this,
len = O.value.length,
//firefox is a big p.o.s. these days... it is keypress & others are keydown
lastWasKey = /key(press|down)/i.test(CURRENT_EVENT.type);
if (O.id == 'curl' && O.value == O.defaultValue) {
O.value = 'http://';
len = O.value.length;
setSelectedText(O, len, len);
}
if (lastWasKey && CURRENT_EVENT.keyCode == 9/*tab key*/) {
window.setTimeout(function () {
setSelectedText(O, len, len);
}, 0);
return false;
}
}
});
}
}
});


And then change this:
$(document).ready(function(){
$("#curl").focus(function(){
if( this.value == this.defaultValue ) {
$(this).val("http://");
}
});
$("#commentForm").validate();
});


TO:
$(document).ready(function(){
$("#commentForm").validate();
});


And then I believe it is all ironed out, good luck!

Rain_Lover

5:30 pm on Apr 24, 2011 (gmt 0)

10+ Year Member Top Contributors Of The Month



Works with no problem -- even in IE6! Thank you! :-)