I am looking to do the following
Disallow text selection inside of an input element
Do not show the cursor carrot inside of an input
i cannot simply blur the input on click, focus must remain in the input. There is probably a way to do this in just IE, i would of course rather have a cross browser solution but ill settle for IE (or FF) only solution.
Here is a demo page where you can see why i might need this functionality:
http://programmingdrunk.com/current-projects/dropdownReplacement/
if you click on the dropdowns in the first row on page, you will see the carrot inside the dropdown which looks funny. (this wont happen in chrome, but will in FF or IE)
A bit hacky, but:
onclick="this.selectionStart=this.selectionEnd=-1;"
onselect="this.selectionStart=this.selectionEnd=-1;"
Seems to work in Firefox (3.6.3).
Do us all a favor and hide it from the HTML, though (attachEvent magic).. And don't tell anyone I suggested this :)
I use the following function in my code, it's not JQuery but it should be fairly easy to convert:
function disableSelection(elm) {
// Disable the selection of `elm` - should work for all major browsers except Opera
// which doesn't seem to allow disabling selections unless the mousedown
// events etc are cancelled as far as I know
// Disable the select start event
elm.onselectstart = function() {
return false
}
// Disable in IE - see http://msdn.microsoft.com/en-us/library/ms534706(VS.85).aspx
elm.unselectable = "on"
// Disable in Mozilla - see https://developer.mozilla.org/en/CSS/-moz-user-select
elm.style.MozUserSelect = 'none'
// Disable Safari/Chrome
// See http://help.dottoro.com/lcrlukea.php
elm.style.webkitUserSelect = 'none'
// Disable in other browsers
elm.style.userSelect = 'none'
// Display a normal cursor
elm.style.cursor = "default"
}
You can disable text selection in some non IE browsers with CSS user-select.
-webkit-user-select:none;
-k-user-select:none;
-moz-user-select:moz-none;
user-select:none;
Not sure about IE.
As far as the effect you're trying to achieve, how about making the input invisible and have a div on top of the input that displays the value of the input?
If you need to change the value in the input, a click event on the div would direct focus to the input, and a keypress event would update the div.
Haven't tried it, but seems like it should work.
EDIT:
Use CSS to render the input invisible in order to retain tabbing functionality.
(Example assumes background is #FFF)
#myInput {
border-width:0;
color:#FFF;
background:#FFF;
outline:0;
}
Related
I am creating a drag-&-drop function on a WYSIWYG component (based on Medium-Editor), works fairly ok, except on firefox where it seems to have some issues.
The feature show allow the user to drag some "widgets" (basically just divs) and change its place, the mechanisms is based on using the dataTransfer of native JS drag-&-drop, then, once the user is ready to drop above a given paragraph (because there's a lot of text around), the widget is added after the paragraph.
One issue in particular remains perplexing: on drop, for Firefox, the paragraph splits right where the cursor of the mouse was pointing, hence creating two new paragraph elements from one old and unique paragraph elements. In Chrome, it works just fine. Using the After method, that's should be the nominal behavior.
The core of the code is as follows:
Draggable widgets:
// Drag event listener
widgetElement.addEventListener('dragstart', event => {
// #ts-ignore
document.dragSourceElement = widgetElement
event.dataTransfer.effectAllowed = 'move'
event.dataTransfer.dropEffect = 'move'
event.dataTransfer.setData('text/html', widgetElement.outerHTML)
})
"Target" paragraphs:
// Drop event listener
paragraphElement.addEventListener('drop', _ => {
if (document.dragSourceElement && document.dragSourceElement !== paragraphElement) {
if (this.detectBrowser() === 'chrome') {
paragraphElement.after(document.dragSourceElement)
}
// Treat Firefox singularly since it interprets "move" in drag&drop as "copy" (For some reason) through the use of a isParagraph flag.
if (this.detectBrowser() === 'firefox') {
paragraphElement.after(document.dragSourceElement)
// isParagraphe is true, remove the old dragged widget and create a new one on the drop position. isParagraph is set in an argument of a higher order method.
if (isParagraphe) {
document.dragSourceElement.remove()
}
}
}
return false
})
Here's a gif of what happens on chrome:
Here's a gif of what happens on firefox:
Ok, so why the difference ? It might juste be difference in implementation of Medium-editor on the different browsers, but I suspect Firefox manages the events differently.
What would be the cleanest way to harmonize the behavior and make sure I get a chrome-like behavior in Firefox (and let's try to avoid, unless necessary, parsing the whole text to reconstitute old states of paragraphs xD).
Thanks.
How can I get the selected text inside an input box in Firefox?
This is apparently a fundamental difference in the way that JavaScript works between Chrome and Firefox. To reproduce:
Navigate to www.google.com
Open JS console
Type following line: document.addEventListener("selectionchange", () => console.log(document.getSelection().toString()))
Type "text" into Google search box and do not hit enter
Use the mouse to select different portions of the "text" in the search box
In Chrome, you will see the event raised for selected text within the input element. This is consistent across various web pages that use input fields. In Firefox, the event is raised for selections outside the input, but when the text in the box is selected, no event is raised.
I have not found any explicit reference to this difference in any Mozilla documentation, nor have I found mention of it on any other web page.
Related but different question
Firefox Web Extension “selectionchange” is an older question, and the dom.select_events.enabled config attribute is now defaulted to true in FF 56. There is a second config attribute, dom.select_events.textcontrols.enabled that seems like what I'm looking for, but changing that value to true doesn't seem to have any effect.
Additional info (Edit 1)
Apparently there isn't even a way to get selected text in a textbox in FF? The following code also doesn't work:
setInterval(() => console.log(document.getSelection().toString()), 1000)
In FF, this will never return the selected text in an input field. In Chrome, it will.
Is this just a feature gap in FF? Is there no other way to extract selected text from a form field?
For me, setting dom.select_events.textcontrols.enabled in firefox did not enable document.addEventListener("selectionchange"... events for within textarea changes, but what it enabled was <textarea onselectionchange="...">.
By adding your handler to both, and toggling that firefox flag, you should get something that works in Chrome, Safari, and Edge (through document selectionchange) and in Firefox (through textarea onselectionchange).
I wasn't able to detect document.onselectionchange events from inputs or textareas in Firefox (86), but I am able to detect select events from the textareas themselves (onselect).
(Thanks to this post [in Russian] for the answer)
Based on this answer I have been able to create the following:
window.addEventListener('mouseup', function () {
selection = getSelectedText();
});
function getSelectedText() {
let elem = document.activeElement;
let elemType = elem ? elem.tagName.toLowerCase() : 'none';
if (elemType === 'input' || elemType === 'textarea') {
return elem.value.substr(elem.selectionStart, elem.selectionEnd - elem.selectionStart);
}
if (window.getSelection) {
return window.getSelection().toString();
}
if (document.selection && document.selection.type !== 'Control') {
return document.selection.createRange().text;
}
return '';
}
My testing so far shows that this seems to work well for both Chrome and Firefox.
Suppose I have a button, which goes into a down state when someone clicks on it, but before the mouse is released.
Now suppose instead that someone presses the 'a' key, I want the button to go into the down state, until the key is released, at which point it is triggered. Is this possible?
After dooing some research here is the final answer I got:
You can trigger mousedown or mouseup events on a button element using keyup and keydown
if your button is programmed to change its style according to these events than you are good to go.
See this fiddle example: http://jsfiddle.net/FwKEQ/15/
Note that if you use jQuery's UI components than it does work. But for standard buttons there is no way that you can move them to their pressed state using javascript
html:
<button id="jQbutton">Press 'A' to move me to pressed state</button>
Javascript:
<script>
$( "#jQbutton" ).button();
$(document).keydown(function(event) {
if ((event.keyCode === 97)||(event.keyCode === 65))
$("#jQbutton").mousedown();
});
$(document).keyup(function(event) {
if ((event.keyCode === 97)||(event.keyCode === 65))
$("#jQbutton").mouseup();
});
</script>
EDIT:
There might be a hack that we could utilize:
using accesskey for the button element and then try to simulate the accesskey press (that i am not sure if possible)
here is where i'm at so far http://jsfiddle.net/FwKEQ/28/
EDIT 2:
So looking further into this topic i have found the following:
Default buttons (without styles) are rendered by the OS, I was not able to find a formal proof for that but if you try to load the same page using a mac OS you'll get mac OS style buttons while in windows you will get the "ugly" gray button.
Because the default buttons are rendered by the OS they comply to OS events meaning events that are sent by the browser and are trusted.
this is not true for custom styled buttons as they comply to CSS an JS to change their appearance on press that is why the JQ button is affected by JS.
so to summarize you would need a trusted press event to fire on a default button to change its style and that cannot be done due to security constraints.
read a bit more about trusted events here: http://www.w3.org/TR/DOM-Level-3-Events/#trusted-events
and if someone could find a formal reference with regards to the default buttons being rendered by the OS please comment or edit this answer.
Unfortunately the rendering of the active state on default buttons neither
is a simple matter of css styling nor can be easily changed by applying
javascript.
An option to do this on default buttons is to use the hotkeys jquery plugin: https://github.com/jeresig/jquery.hotkeys or implement alternative key codes for different browsers.
and to apply 50% opacity to the default button when pressed (to indicate the keydown).
(To me it seems almost perfect ;-) It probably is as good as it can easily get to work across platforms and browsers using default buttons.
jsfiddle DEMO
and the code ...
html:
<button id="test">Test Button</button>
Selected: <span class="selected" id="out"></span>
javascript:
$('#test').click(function () {
fn_up();
});
fn_down = function(event){
$('#test').css("opacity", 0.5);
$('#test').focus();
event.preventDefault();
}
fn_up = function(event){
$('#test').css("opacity", 1);
$('#out').append(" test");
event.preventDefault();
}
//to bind the event to the 'a' key
$(document).bind('keydown','a', fn_down);
$(document).bind('keyup','a', fn_up);
//to get the same effect with the 'space' key
$(document).bind('keydown','space', fn);
$(document).bind('keyup','space', fn2);
In the fiddle I apply it to the space button and the mousedown/up to achieve the same effect with all events (but you could just use it with the 'a' key ... this is a matter of taste).
Here is a jsfiddel that shows how it's done using jQuery: http://jsfiddle.net/KHhvm/2/
The important part:
$("#textInput").keydown(function(event) {
var charCodeFor_a = 65;
if ( event.which == charCodeFor_a ) {
// "click" on the button
$('#button').mousedown();
// make the button look "clicked"
$('#button').addClass('fakeButtonDown');
// do some stuff here...
// release the button later using $('#button').mousedown();
}
});
The button event is triggered when entering "a" in the input field. But as Mark pointed out you need to fake the styling for the clicked button because the browser doesn't do it.
Edit: I'm not sure if you're using jQuery in your project. I just wanted to show that it is possible at all. If it can be done with the jQuery library there is also a way to do it in pure javascript. ;)
I am getting a problem when using onblur and onfocus event of textbox in Firefox.
In Opera. it works as expected (e.g. onfocus is called first, then onblur).
In Firefox, onblur is called first, then it calls onfocus. This should not happen.
How can this be fixed?
It's good to know that some browsers are broken in this way. Consider the following code.
<input type="text" id="t1" onfocus="is_editing=true" onblur="is_editing=false" />
<input type="text" id="t2" onfocus="is_editing=true" onblur="is_editing=false" />
In Opera, if you click on either textfield, is_editing will be true. If you then tab to the other textfield... is_editing will be false!
The same happens if you replace the above variable assignments with function calls: enableEditing() and disableEditing(), for example: you tab from one field to another, and editing becomes disabled. That is clearly not what people would want!
To avoid this, most browsers now support document.activeElement, which you need to use to do something really nasty like so:
function enableEdit() { is_editing = true; }
/* Can't just unset is_editing here, since broken browsers may
call onblur /after/ onfocus when tabbing between two text elements.
So, we only unset if we don't have a text element focused */
function disableEdit() {
if (!document.activeElement) {
/* For old & broken browser support, could traverse every DOM elem to set
elem.onfocus = function(){ document.activeElement = elem; }
For the meantime, I just assume they aren't broken. */
is_editing = false;
}
// If not focused on a text input type, then it's OK to unset.
else if ("text" != document.activeElement.type) {
is_editing = false;
}
}
Obviously, replace the is_editing assignments with whatever you wanted to happen onblur/onfocus.
With jQuery, you can also apparently use $("*:focus") in the same way as I used document.activeElement above.
I am on Salesforce (visualforce) and using a custom autocomplete Javascript. My requirement is to trigger auto complete search on a text field element2 as soon as a selection is made from suggestions on another text field element1.
Since I need to be able to scroll through the auto suggestions list using a keyboard, I need to have focus on the particular field. Am currently doing a element2.focus() just after a selection is made on element1 and triggering the auto suggest search on element2.
Also, on these fields, when the search is running and the user manually focuses on the field, the auto suggestion collapses - this is an indication of cancelling the search. Because of this, I cannot trigger the search and then call element2.focus()
Here's what am experiencing in different browsers:
Chrome/Firefox 3.5, 4/Safari 5.0.3:
Select an option from suggestions under element1
Value in field changes
Suggestions collapse
Field blurs, but not sure where focus goes. Probably window
IE 8:
Select an option from suggestions under element1
Value in field changes
Suggestions collapse
Field blurs and element2 takes focus
Search fires for this field
Also, the above difference in behaviour is only when am selecting using a mouse click. When using a keystroke (up/down then enter) this works as expected in all browsers. The same set of javascript methods are executed on both mouse and keyboard selection.
An interesting 'fix' I found for this is calling element2.focus() after, say, 100 ms using setTimeout(). Am guessing this is because element1's onblur is disrupting element2.focus() but am not really happy using this.
Well, any ideas?
Code Samples:
//mouseclick handler
function handleMouseClick(event){
element1.value = (event.target)?event.target.textContent:event.srcElement.innerText;
callback();
// kills the children and hides the div containing the suggestions
hideAutoComplete();
}
function callback() {
element2.value = '';
element2.focus();
}
Can you use a framework? They really take the pain out of cross-browser compatibility for events. Here's a short example using jQuery that seems to do what you want. Any of the major frameworks would probably work just as well for this.
<html>
<head>
<title>Testing some JS behavior</title>
</head>
<body>
<form id="fooForm">
<label for="a">A: </label><input id="a"/><br />
<label for="b">B: </label><input id="b"/><br />
</form>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
<script>
$('#b').focus(function(e) {
alert("Focusing on b now.");
});
$('#a').blur(function(e) {
alert("Doing my business on element A.");
$('#b').focus();
// Stop bubbling, just in case this got triggered by them clicking into B
return false;
});
</script>
</body>
</html>