How to detect a file input has been cleared programaticaly - javascript

I have file input that is added by a third party plugin, the uploaded file has a custom preview, which i can click on a remove button within the preview element to clear the input, and this preview is injected to the Dom uppon file addition. when i try to detect the action of clearing the input , neither change nor ìnput events are triggered.
Please note that i could normally listen to the change if file has been added only.
I have tried this:
const fileInput = document.getElementById('thumb');
fileInput.addEventListener('input', (e) => {
if( fileInput.files.length > 0 )
{
const file = fileInput.files[0]; // Get the first selected file
const blobURL = URL.createObjectURL(file);
}else{
console.log('removed');
}
});
And sure i tried to replace input with change but no result

Since you are having problems addressing the remove button and listen for its click event, and since a regular MutationObserver would be listening to the change of an attribute and not strictly an object's property value being changed,
you may try redefining the setter for the value property so that every time it will be changed programmatically, it will also perform your code.
You will find further details here (among the posted answers):
Detect input value change with MutationObserver
Here in this demo there's both the change event listener and the new property setter defined. When you'll try to clear the input using the corresponding button, the attempt to change its value property will be intercepted printing the value was changed programmatically! on console:
//adds a canonic change event handler to the #thumb element
document.getElementById('thumb')
.addEventListener('change',()=>{
console.log('file input has changed!');
});
//clear the input file programmatically (when the corresponding button is pressed)
function clearFile(){
document.getElementById('thumb').value = "";
}
//on document ready
document.addEventListener('DOMContentLoaded',()=>{
//overrides the setter for the value property of the #thumb element
const fileInput = document.getElementById('thumb');
const originalSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype,'value').set;
Object.defineProperty(document.getElementById('thumb'), "value", {
set: function (newValue) {
//so that it will print on console..
console.log('the value was changed programmatically!');
//and perform its original designation
return originalSetter.call(this, newValue);
}
});
});
<input type="file" id="thumb">
<button onclick="clearFile();">CLEAR PROGRAMMATICALLY</button>

The solution I'm thinking of is to detect changes in the value of the input file by using a Javascript setInterval every 1 ms.
let fileInput = document.getElementById('thumb');
let check = document.getElementById('check');
let clear = document.getElementById('clear');
let currentValue;
let lastValue = '';
setInterval(()=>{
currentValue = fileInput.value;
if (lastValue !== currentValue && currentValue === '') {
console.log('interval: file removed');
} else if (lastValue !== currentValue && currentValue !== '') {
console.log('interval: file added');
}
lastValue = currentValue;
},1);
check.onclick = () => {
if(fileInput.files.length > 0) {
const file = fileInput.files[0]; // Get the first selected file
const blobURL = URL.createObjectURL(file);
console.log(blobURL);
console.log('onclick: file is exist');
} else {
console.log('onclick: file is empty');
}
}
clear.onclick = () => {
fileInput.value = '';
}
<input type="file" id="thumb">
<button id="check">Check</button>
<button id="clear">Clear</button>

Related

Preventing the user from deleting in an input field

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

Converting javascript element to html while keeping the associated events

I have created a checkbox directly in javascript, and binded a click event the following way :
let checkBox = document.createElement('input');
checkBox.onclick = (e) => {
console.log("click", e);
};
Now I would like to convert this element to plain html, while keeping the associated event. I now I can call checkBox.outerHTML to get the associated html, but the event would disappear.
Is there a way to do the same thing without removing the attached event ?
I don't know why would you need such an approach when you simply can append that element where ever you want. Yet, it is still simple to just fix it the way it is.
Instead of assigning an event, you should assign an attribute like this:
const checkBox = document.createElement('input');
checkBox.setAttribute("onclick", "cbxClicked(event)");
function cbxClicked(e) {
console.log("click", e);
};
console.log(checkBox.outerHTML); // <input onclick="cbxClicked(event)">
Tested well on chrome.
The recommended way is this
window.addEventListner("load",function() {
document.getElementById("checkboxContainer")
.addEventListener("click",function(e) {
const tgt = e.target;
if (tgt.type && tgt.type==="checkbox") {
console.log("click",tgt)
}
});
});
Now you can create your checkboxes before or after load
window.addEventListener("load", function() {
const container = document.getElementById("checkboxContainer");
container.addEventListener("click", function(e) {
const tgt = e.target;
if (tgt.type && tgt.type === "checkbox") {
console.log("click", tgt)
}
});
const inp = document.createElement("input")
inp.type = "checkbox";
inp.value = "dynamic";
container.appendChild(inp);
});
<div id="checkboxContainer">
<input type="checkbox" value="static" />
</div>

Appended item is instantly removed

I am trying to understand the basics of eventListeners, i have created a simple form where i just want to add a value of an input to a UL, however when i append the value, i am able to see it in the list for a brief second and then it is instantly removed, i cannot figure out why, could anyone help?.
const submitButton = document.querySelector('#add-task-btn');
const clearButton = document.querySelector('#remove-task-btn');
const item = document.querySelector('#task');
const taskList = document.querySelector('.collection');
allEventListeners();
function allEventListeners(){
submitButton.addEventListener('click', function(){
if (item.value === ''){
alert('Please add a task')
};
const li = document.createElement('li');
li.appendChild(document.createTextNode(item.value));
taskList.appendChild(li);
item.value = "";
})
}
You just need to provide an event parameter for your handler function and then call preventDefault()
submitButton.addEventListener('click', function(ev){
ev.preventDefault(); // prevent the page submit
//...
});

Trigger change event on hidden field when i don't know the moment of change with jQuery?

I have a hidden input in my HTML code and i want to know when the input value has changed.
<input type="hidden" id="myInputHidden" />
I can make something like this:
$('#myInputHidden').on('change', function() {
alert('triggered');
});
In the first place, this doesn't work and in many posts i have read that i must trigger manually the event.
The problem is that i don't know when (and where) the input value is changed so i cannot trigger that event.
The only way to implement a change event to a hidden field is by dirty-checking, e.g:
(function() {
var myHidden = document.getElementById('myInputHidden'),
currentValue = myHidden.value;
setTimeout(function myHiddenOnChange() {
if (myHidden.value !== currentValue) {
currentValue = myHidden.value;
myHiddenChanged.call(myHidden);
}
setTimeout(myHiddenOnChange, 30);
}, 30);
function myHiddenChanged() {
// that's your hidden field's 'change' event
}
})();
I don't recommend it, but another approach is to override the HTMLInputElement.prototype descriptor:
(function() {
var _htmlInputElementValue = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value'),
_htmlInputElementValueSet = _htmlInputElementValue.set,
_ev = document.createEvent('Event');
_ev.initEvent('change', true, true);
_htmlInputElementValue.set = function() {
_htmlInputElementValueSet.apply(this, [].slice.call(arguments));
if (this.type === 'hidden') {
this.dispatchEvent(_ev);
}
}
Object.defineProperty(HTMLInputElement.prototype, 'value', _htmlInputElementValue);
})();
Doing that, anytime someone changes the value property of a hidden field, it triggers the change event, so, if you're listening to that event, your code will start working.

How to clear HttpPostedFileBase with jquery if image isn't valid

I want to set my file input element to null if the file isn't a jpg, jpeg, etc., But when I set the value of the element that holds HttpPostedFileBase to null it re triggers the change event and creates errors. is there any way to get around this?
I thought I could check at the beginning of the change event to see if the value is null but it doesn't work.
here is my html element (there are two because I'm hiding the upload text box and just displaying it as a button)
#Html.TextBoxFor(model => model.StudentImageFileBase, new { #type = "file", id = "selectedFile", style = "display: none;" })
<input type="button" value="Browse For Image" class="btn" id="pictureupload"/>
here is my javascript that checks the image type.
$(function () {
console.log("ready!");
alert("picture input function entered");
$("#pictureupload").click(function() {
document.getElementById('selectedFile').click();
});
$("#selectedFile").change(function() {
//this doesn't work
var imgVal = $('selectedFile').val();
if (imgVal == '')
return false;
//check whether browser fully supports all File API
if (window.File && window.FileReader && window.FileList && window.Blob) {
//get the file size and file type from file input field
var fsize = $('#selectedFile')[0].files[0].size;
var ftype = $('#selectedFile')[0].files[0].type;
var fname = $('#selectedFile')[0].files[0].name;
switch (ftype) {
case 'image/png':
case 'image/gif':
case 'image/jpeg':
case 'image/pjpeg':
alert("Acceptable image file!");
break;
default:
alert('Unsupported File!');
$('#selectedFile').val(null); //here is where the onchange gets triggered again and
return false;
}
var oFReader = new FileReader();
oFReader.readAsDataURL(document.getElementById("selectedFile").files[0]);
oFReader.onload = function (oFREvent) {
document.getElementById("uploadPreview").src = oFREvent.target.result;
};
} else {
alert("Please upgrade your browser, because your current browser lacks some new features we need!");
return false;
}
});
});
So I think I figured it out!
I added the line below if the file is not a jpg/gif/etc
default:
alert('Unsupported File!');
$("#selectedFile").replaceWith($("#selectedFile").clone(true));
return false;
And it clears the httppostedfilebase memeber, so when I do a post back to my server the value is null. Maybe this isn't a good solution but it's all I have found so far. Just clearing out the element by setting its value re-triggers the change event which is what I am trying to avoid. Unless there is some simple logic I could put into the change event that would combat against it?

Categories