Preventing the user from deleting in an input field - javascript

I want to have a text field (<input> in HTML) that the user can only use to write text, but when deleting the text, it should prevent him from updating the state of the input element.
<input type="text" />
I couldn't come up with a solution except to detect the user key presses on the keyboard, i.e, using the onKeyUp attribute, and watch the user keystrokes until he presses the Backspace character:
const input = document.querySelector('input')
input.addEventListener('keydown', (e) => {
const keyCode = e.keyCode
if (keyCode === 8) console.log("Backspace pressed!")
})
<input type="text" />
However, there's a missing part here, even though the user won't be able to clear the input content by the Backspace key, he can instead use the Del key.
So I would have then to handle the Del key the same way I handle the Backspace key.
Now, there's another problem, which is trying to modify the input content by overwriting the content as follows:
and then, after that, I will have to prevent the user from editing the text by cutting the content (which I have no idea how to do).
So, what are the alternatives?
Please feel free to use HTML, JavaScript, CSS.
If you're using React, I was able to solve this problem simply by checking if the length of the input value is shorter than the one being memorized in the state, then prevent the user from updating the state:
The answer resides between controlling the value prop (or attribute if you wish) and the onChange handler.
const [inputVal, setInputVal] = useState('')
const handleChange = (e) => {
const value = e.target.value
if(value.length < inputVal.length) return // prevent modifications
setInputVal(value)
}
return <input value={inputVal} onChange={handleChange}/>
Now, the question is, how to do the same when using only HTML/JavaScript?

Perhaps something like this? Then it doesn't matter what they press.
const input = document.querySelector('input')
let previousState = input.value;
input.addEventListener('keydown', (e) => {
if ( previousState.length > input.value.length ) input.value = previousState;
previousState = input.value;
})

In the keydown event, put whatever keycodes you want in an array and see if the current keycode is included. Then create a select event, and blur the input when it fires.
const input = document.querySelector('input');
input.addEventListener('keydown', (e) => {
if ([8, 46].includes(e.keyCode)) {
e.preventDefault()
}
})
input.addEventListener('select', (e) => {
e.target.blur()
})
<input type="text" />

#dqhendricks answer is in the right way, but misses some possibilities:
User can hit backspace (reduce length by one) (covered)
User can select a piece of text and hit backspace or other key (covered)
User can select a piece (or all text) and replace by a different string with same length. (Not covered)
So my suggestion is:
You should listen for change event, then compare the previous string, if the new string starts with the previous string, it's ok. Otherwise we revert the change.
In code ir should look like:
const input = document.querySelector('input');
let previousState = input.value; // initial state
input.addEventListener('change', (e) => {
if (!input.value.startsWith(previousState)) {
input.value = previousState; // revert change
}
previousState = input.value;
})

you can check the length of the input's value, if it became shorter replace it with the previous value
let value;
input.addEventListener('change', ()=>{
if(value && input.value.length < value.length) input.value=value;
else value=input.value;
}

You must have some piece of code that sets the value from the barcode scanner. Once that is being set, also do input.dataset.barcodeValue="scanresult".
Then to make it sufficiently difficult for a regular user to ruin the barcode, you need to prevent several things:
pasting
cutting
typing over the barcode value
dragging text into the input
pressing delete or backspace which would mess up the barcode value.
All of this is covered in the input event.
const input = document.querySelector('#bc-value');
function setValueFromBarcodeScanner(val) {
input.value = input.dataset.barcodeValue = val;
input.addEventListener('input', (e) => {
const { barcodeValue } = input.dataset;
if (barcodeValue && !input.value.startsWith(barcodeValue)) input.value = barcodeValue;
})
}
<input type="text" id="bc-value" />
<button type="button" onclick="setValueFromBarcodeScanner('433-224-221-456')">Set value from Barcode Scanner</button>

I think this might work for you:
const inputElement = document.getElementById('write-only-input');
let lastValue = '';
inputElement.oninput = function(e) {
if (inputElement.value.startsWith(lastValue)) {
lastValue = inputElement.value;
} else {
inputElement.value = lastValue;
}
}
<input type="text" id="write-only-input" />

Related

React - Can I verify if user pressed a key or not with on change event?

I need to know if I can verify if user pressed a key or not only with onchange event, because I get the text on input with a QR reader, but there also exists the possibility for the user to manually enter the data.
I have this code (example):
_getValue = (event) => {
let value = event.target.value;
}
render() {
<input type="text" onChange={this._getValue} />
}
So, on _getValue method, which is from an onchange event, I need to check if the change is coming from a key or from the QR reader.
Thank you to all!
You could use the keydown event.
You would probably end up with something like this
_getValue = (event) => {
let value = event.target.value;
}
const handleKeyPress = (e) => {
e.preventDefault();
return;
}
render() {
<input type="text" onChange={this._getValue} onKeyPress={handleKeyPress} />
}
You can read more on the event on MDN

Why does cursor move to start of input field when value is changed dynamically?

I have an input field in my react application like below. suggestedTerm and searchTerm are coming from component's state. searchTerm state is being set in onChange handle. suggestedTerm state is being set when i navigate up or down in autocomplete suggestion list.
<input value={suggestedTerm || searchTerm}
onChange={handleInputChange}
onFocus={() => {
setShowFlyout(true);
clearActiveSuggestion();
}}
onKeyDown={handleInputKeyDown}
ref={searchInput}
/>
Here through onKeyDown handler, i am handling up and down arrow key events to navigate through the autosuggestion suggestions list that is being produced while keeping the focus on the input field. That was done basically to cater accesibility.
The requirement is to set the selected suggestion on the input field as we navigate through the autosuggestion list. However, the issue i am facing here is that my cursor moves to the beginning of the input field whenever i set suggestedTerm state which in return sets the input field's value while navigating up using up arrow key. This does not happen when i navigate through down key.
Attaching here my up key and down key logic
if ((e.key === "ArrowUp" || e.keyCode === 38) && !isEmpty(suggestions)) {
const focusedItem = getActiveSuggestion();
const index = focusedItem[0].index - 1;
if (!isEmpty(focusedItem) && focusedItem[0] && focusedItem[0].index > 0) {
setActiveSuggestion(index);
} else {
//Clear active suggestions if up is pressed while focus is on first element
clearActiveSuggestion();
}
if ((e.key === "ArrowDown" || e.keyCode === 40) && !isEmpty(suggestions)) {
const focusedItem = getActiveSuggestion();
//Set first suggestion active
if (isEmpty(focusedItem)) setActiveSuggestion(0);
else {
if (
!isEmpty(focusedItem) &&
focusedItem[0] &&
focusedItem[0].index < (suggestions && suggestions.length - 1)
) {
setActiveSuggestion(focusedItem[0].index + 1);
} else {
//Set first suggestion active when focus is on last item already and down key is pressed
setActiveSuggestion(0);
}
}
}
const clearActiveSuggestion = () => {
setSuggestions(suggestions.map(suggestion => ({ ...suggestion, active: false })));
setSuggestedTerm("");};
const getActiveSuggestion = () => {
return suggestions.filter(suggestion => suggestion.active);};
const setActiveSuggestion = activeItemIndex => {
const updatedSuggestion = suggestions.map(suggestion => {
if(activeItemIndex === suggestion.index)
setSuggestedTerm(suggestion.dq);
return {...suggestion, active: activeItemIndex === suggestion.index};
});
setSuggestions(updatedSuggestion);};
First, i do not understand why my input field is setting cursor to start of the input field even though i am changing the state and state change should reset value of input field.
Second, i searched a number of ways to manually set the cursor using setSelection method and manually setting input field's value using ref but nothing is changing the behavior.
Can anybody figure out the issue here?
Thanks
Figured out the root cause. Actually, it is a default behavior of an input field to take cursor to start of the string in an input field when up arrow is pressed on it so i simply added
e.preventDefault();
and that prevented the default behavior of the input field to move the cursor. It had nothing to do with how i am setting the state or setting the selectionRange of the input manually.

How to stop an event from being further processed in React

I have a text input in my React app which I don't want to take inputs which are greater than 100. For example, If the entered value is 105, an alert is created and the event is terminated i.e changing input value is not gonna happen. Now I couldn't find a way to do this inside onChange function. Any help would be highly appreciated.
<input onChange={handleChange} name="t"/>
handleChange = e => {
if(e.target.value > 100){
alert("High")
//Here I want to stop event so that changing text in the input doesn't happen
}
}
Make it a controlled input and only set the value if a condition is met.
const App = () => {
const [value, setValue] = React.useState("");
const handler = (e) => {
const value = Number(e.target.value);
value <= 100 && setValue(value);
};
return (
<input onInput={handler} type="number" value={value} />
);
}
ReactDOM.render(<App/>, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>
If I'm understanding you properly, if a specific condition is not met, you want to prevent the input from reflecting the text the user just entered.
In order to accomplish this, you'll need to control your input's value via state.
That means doing something like this:
<input onChange={handleChange} name="t" value={this.state.tInput}/>
handleChange = e => {
if(parseInt(e.target.value) > 100) {
alert("High")
// set input state here to previous value to re-render and remove unwanted input values from input
return this.setState(({tInput}) => ({tInput}))
}
return this.setState({tInput: e.target.value})
}
handleChange = e => {
if(e.target.value > 100){
alert("High");
e.target.value = "";
}
else {
// your normal processing
}
}
should work.
Explanation:
Your code would simply not be executed in case the if condition is true.
The line e.target.value = "" doesn't actually "not show" the users input (as asked for in comment), but rather overrides it with empty string.
Mention:
This solution has nothing to do with React, but rather works in any context.

contentEditable div Multilines issue on FireFox

I have a div with `contentEditable="true"
<div id="msgInput" contentEditable="true"></div>
When the user types in it and presses 'enter' it appends what the user typed to another div
<div id="output"></div>
But I need it so the user can type multiple lines (with shift+enter) without firing the 'keydown' event on the 'enter' key
Here is how I achieved this: (please note that I added the 'input' event Listener because I need to save what the user types in my state variable)
let input = document.getElementById('msgInput');
let output = document.getElementById('output');
let state = '';
input.addEventListener('input', function(event) {
state = event.target.textContent;
});
input.addEventListener('keydown', function(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.target.innerHTML = '';
output.innerHTML = state;
event.preventDefault();
}
});
On Chrome, this is working fine,
for example, If I type "first"
then shift+enter,
then type "second"
and then press enter, it will be appended like this:
first
second
And that's exactly what I want.
But on firefox, it's appended in one line (merged as one word)
firstsecond
I need it in firefox to have the same behavior as in chrome
here's a fiddle for this
https://jsfiddle.net/Lrnx24tb/2/
You could try replace textContent for innerText:
input.addEventListener('input', function(event) {
state = event.target.innerText;
});

Display message in Chrome if wrong type is entered in input field

In Chrome (55.x) if a user attempts to enter mismatched type into an input (in my case a number input) nothing outwardly appears to happen. To enhance usability I'd like to display a popup to let users know they're trying to enter invalid data rather than have them thinking the input is 'broken'.
This is easily achieved with pure JS in FF (which allows mismatched type to be entered, it just isn't valid):
input.addEventListener('input', function() {
if (!this.checkValidity()) {
this.value = '';
console.log('please enter a number!');
}
}
Because Chrome doesn't actually input anything, however, the validity check always passes as the input is empty; it doesn't appear to do anything with the incorrect input except ignore it.
Is there any way to override this behaviour, or otherwise achieve the intended effect?
Sure, you could do a listener for keyup and it will give you the key that was pressed.
<script>
var numInput = document.getElementById("num");
numInput.addEventListener("keyup", function(e) {
if (isNaN(String.fromCharCode(e.which))) {
alert("Must be a number");
}
});
</script>
How about setting a last input value and check like this?
let input = document.querySelector('input');
let lastInput = input.value;
input.addEventListener('input', (e) => {
if (e.target.value === lastInput) {
alert("Input must be a number");
}
lastInput = e.target.value;
});

Categories