How to use the UndoManager to determine if document has unsaved changes - javascript

I'm quite new to the ACE editor and javascript in general however I have managed to achieve most of what I intended apart from the following:
I would like to enable or disable a 'save' button depending on whether there are outstanding modifications to the document and I have attempted to do this with 'change' event:
UndoManager.reset();
$('#save').addClass("disabled");
editor.on('change', function() {
if (UndoManager.hasUndo()) {
$('#save').removeClass("disabled");
}
else {
$('#save').addClass("disabled");
}
});
On loading a document the 'disabled' class is removed immediately.
Many thanks in advance if anyone can show me how it should be done.

Call editor.session.getUndoManager().reset() after loading the document
and use isClean, markClean methods on undoManager.
var saveButton = document.getElementById("save")
var editor = ace.edit("editor")
// using input event instead of change since it's called with some timeout
editor.on("input", function() {
saveButton.disabled = editor.session.getUndoManager().isClean()
});
saveButton.addEventListener("click", function() {
editor.session.getUndoManager().markClean()
saveButton.disabled = editor.session.getUndoManager().isClean()
})
<script src="http://ajaxorg.github.io/ace-builds/src/ace.js"></script>
<button id="save">save</button>
<div id="editor" style="height:200px"></div>

Your solution has a disadvantage: after saving, you are not able to undo. Most modern editors allow undo after saving.
I suggest that you record down the original text and do a comparision whenever text changes. if text equals origin, disable save button.

Related

Tampermonkey: Trigger event does not work for element

I'm trying to add some functionality using Tampermonkey on top of a providers angular application but I'm stuck at this simple thing. I can't replicate the issue using CodePen so we're going to have to go for theories and suggestions. I'll try to be as specific as I can.
Adding this interval when the page loads to check when an input with the id serialNumberInput is available. Then I'm adding a dropdown to the form, and attach an onChange event to it to update the serial input field with the value of the selected option. However, the trigger parts just never happens. It does work when I enter them manually, but not with the script.
var populateSerialNumbersTimer = setInterval(function(){
var serial = $("input#serialNumberInput");
if($(serial).length >= 1){
$(serial).css("display", "inline").css("width", "50%");
$(serial).after(deviceToSerialSelectionHTML);
$("select#deviceToSerial").on("change", function(){
$(serial).val($("select#deviceToSerial").val());
$(serial).trigger("change");
$(serial).trigger("blur");
});
clearInterval(populateSerialNumbersTimer);
}
}, 200);
I've thought about it and considering how the serial number ends up in the text field the field must be accessible. Maybe it's that the events that I'm trying to trigger has not been declared at the time of the function declaration?
Suggestions much appreciated.
It looks like jQuery tries to cache the event somehow. This is how I solved it with native javascript in case someone else is interested:
function triggerEvent(e, s){
"use strict";
var event = document.createEvent('HTMLEvents');
event.initEvent(e, true, true);
document.querySelector(s).dispatchEvent(event);
}
$("select#deviceToSerial").on("change", function(){
serialNumberInput.val($("select#deviceToSerial").val());
triggerEvent("change", "input#serialNumberInput");
triggerEvent("blur", "input#serialNumberInput");
}

Detect when upload file dialog is closed [duplicate]

How can I detect when the user cancels a file input using an html file input?
onChange lets me detect when they choose a file, but I would also like to know when they cancel (close the file choose dialog without selecting anything).
While not a direct solution, and also bad in that it only (as far as I've tested) works with onfocus (requiring a pretty limiting event blocking) you can achieve it with the following:
document.body.onfocus = function(){ /*rock it*/ }
What's nice about this, is that you can attach/detach it in time with the file event, and it also seems to work fine with hidden inputs (a definite perk if you're using a visual workaround for the crappy default input type='file'). After that, you just need to figure out if the input value changed.
An example:
var godzilla = document.getElementById('godzilla')
godzilla.onclick = charge
function charge()
{
document.body.onfocus = roar
console.log('chargin')
}
function roar()
{
if(godzilla.value.length) alert('ROAR! FILES!')
else alert('*empty wheeze*')
document.body.onfocus = null
console.log('depleted')
}
See it in action: http://jsfiddle.net/Shiboe/yuK3r/6/
Sadly, it only seems to work on webkit browsers. Maybe someone else can figure out the firefox/IE solution
So I'll throw my hat into this question since I came up with a novel solution. I have a Progressive Web App which allows users to capture photos and videos and upload them. We use WebRTC when possible, but fall back to HTML5 file pickers for devices with less support *cough Safari cough*. If you're working specifically on an Android/iOS mobile web application which uses the native camera to capture photos/videos directly, then this is the best solution I have come across.
The crux of this problem is that when the page loads, the file is null, but then when the user opens the dialog and presses "Cancel", the file is still null, hence it did not "change", so no "change" event is triggered. For desktops, this isn't too bad because most desktop UI's aren't dependent on knowing when a cancel is invoked, but mobile UI's which bring up the camera to capture a photo/video are very dependent on knowing when a cancel is pressed.
I originally used the document.body.onfocus event to detect when the user returned from the file picker, and this worked for most devices, but iOS 11.3 broke it as that event is not triggered.
Concept
My solution to this is *shudder* to measure CPU timing to determine if the page is currently in the foreground or the background. On mobile devices, processing time is given to the app currently in the foreground. When a camera is visible it will steal CPU time and deprioritize the browser. All we need to do is measure how much processing time our page is given, when camera launches our available time will drop drastically. When the camera is dismissed (either cancelled or otherwise), our available time spike back up.
Implementation
We can measure CPU timing by using setTimeout() to invoke a callback in X milliseconds, and then measure how long it took to actually invoke it. The browser will never invoke it exactly after X milliseconds, but if it is reasonable close then we must be in the foreground. If the browser is very far away (over 10x slower than requested) then we must be in the background. A basic implementation of this is like so:
function waitForCameraDismiss() {
const REQUESTED_DELAY_MS = 25;
const ALLOWED_MARGIN_OF_ERROR_MS = 25;
const MAX_REASONABLE_DELAY_MS =
REQUESTED_DELAY_MS + ALLOWED_MARGIN_OF_ERROR_MS;
const MAX_TRIALS_TO_RECORD = 10;
const triggerDelays = [];
let lastTriggerTime = Date.now();
return new Promise((resolve) => {
const evtTimer = () => {
// Add the time since the last run
const now = Date.now();
triggerDelays.push(now - lastTriggerTime);
lastTriggerTime = now;
// Wait until we have enough trials before interpreting them.
if (triggerDelays.length < MAX_TRIALS_TO_RECORD) {
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
return;
}
// Only maintain the last few event delays as trials so as not
// to penalize a long time in the camera and to avoid exploding
// memory.
if (triggerDelays.length > MAX_TRIALS_TO_RECORD) {
triggerDelays.shift();
}
// Compute the average of all trials. If it is outside the
// acceptable margin of error, then the user must have the
// camera open. If it is within the margin of error, then the
// user must have dismissed the camera and returned to the page.
const averageDelay =
triggerDelays.reduce((l, r) => l + r) / triggerDelays.length
if (averageDelay < MAX_REASONABLE_DELAY_MS) {
// Beyond any reasonable doubt, the user has returned from the
// camera
resolve();
} else {
// Probably not returned from camera, run another trial.
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
}
};
window.setTimeout(evtTimer, REQUESTED_DELAY_MS);
});
}
I tested this on recent version of iOS and Android, bringing up the native camera by setting the attributes on the <input /> element.
<input type="file" accept="image/*" capture="camera" />
<input type="file" accept="video/*" capture="camcorder" />
This works out actually a lot better than I expected. It runs 10 trials by requesting a timer to be invoked in 25 milliseconds. It then measures how long it actually took to invoke, and if the average of 10 trials is less than 50 milliseconds, we assume that we must be in the foreground and the camera is gone. If it is greater than 50 milliseconds, then we must still be in the background and should continue to wait.
Some additional details
I used setTimeout() rather than setInterval() because the latter can queue multiple invocations which execute immediately after each other. This could drastically increase the noise in our data, so I stuck with setTimeout() even though it is a little more complicated to do so.
These particular numbers worked well for me, though I have see at least once instance where the camera dismiss was detected prematurely. I believe this is because the camera may be slow to open, and the device may run 10 trials before it actually becomes backgrounded. Adding more trials or waiting some 25-50 milliseconds before starting this function may be a workaround for that.
Desktop
Unfortuantely, this doesn't really work for desktop browsers. In theory the same trick is possible as they do prioritize the current page over backgrounded pages. However many desktops have enough resources to keep the page running at full speed even when backgrounded, so this strategy doesn't really work in practice.
Alternative solutions
One alternative solution not many people mention that I did explore was mocking a FileList. We start with null in the <input /> and then if the user opens the camera and cancels they come back to null, which is not a change and no event will trigger. One solution would be to assign a dummy file to the <input /> at page start, therefore setting to null would be a change which would trigger the appropriate event.
Unfortunately, there's no way official way to create a FileList, and the <input /> element requires a FileList in particular and will not accept any other value besides null. Naturally, FileList objects cannot be directly constructed, do to some old security issue which isn't even relevant anymore apparently. The only way to get ahold of one outside of an <input /> element is to utilize a hack which copy-pastes data to fake a clipboard event which can contain a FileList object (you're basically faking a drag-and-drop-a-file-on-your-website event). This is possible in Firefox, but not for iOS Safari, so it was not viable for my particular use case.
Browsers, please...
Needless to say this is patently ridiculous. The fact that web pages are given zero notification that a critical UI element has changed is simply laughable. This is really a bug in the spec, as it was never intended for a full-screen media capture UI, and not triggering the "change" event is technically to spec.
However, can browser vendors please recognize the reality of this? This could be solved with either a new "done" event which is triggered even when no change occurs, or you could just trigger "change" anyways. Yeah, that would be against spec, but it is trivial for me to dedup a change event on the JavaScript side, yet fundamentally impossible to invent my own "done" event. Even my solution is really just heuristics, if offer no guarantees on the state of the browser.
As it stands, this API is fundamentally unusable for mobile devices, and I think a relatively simple browser change could make this infinitely easier for web developers *steps off soap box*.
You can't.
The result of the file dialog is not exposed to the browser.
When you select a file and click open/cancel, the input element should lose focus aka blur. Assuming the initial value of the input is empty, any non empty value in your blur handler would indicate an OK, and an empty value would mean a Cancel.
UPDATE: The blur is not triggered when the input is hidden. So can't use this trick with IFRAME-based uploads, unless you want to temporarily display the input.
Most of these solutions don't work for me.
The problem is that you never know which event will be triggered fist,
is it click or is it change? You can't assume any order, because it probably depends on the browser's implementation.
At least in Opera and Chrome (late 2015) click is triggered just before 'filling' input with files, so you will never know the length of files.length != 0 until you delay click to be triggered after change.
Here is code:
var inputfile = $("#yourid");
inputfile.on("change click", function(ev){
if (ev.originalEvent != null){
console.log("OK clicked");
}
document.body.onfocus = function(){
document.body.onfocus = null;
setTimeout(function(){
if (inputfile.val().length === 0) console.log("Cancel clicked");
}, 1000);
};
});
/* Tested on Google Chrome */
$("input[type=file]").bind("change", function() {
var selected_file_name = $(this).val();
if ( selected_file_name.length > 0 ) {
/* Some file selected */
}
else {
/* No file selected or cancel/close
dialog button clicked */
/* If user has select a file before,
when they submit, it will treated as
no file selected */
}
});
The new File System Access API will make our life easy again :)
try {
const [fileHandle] = await window.showOpenFilePicker();
const file = await fileHandle.getFile();
// ...
}
catch (e) {
console.log('Cancelled, no file selected');
}
Browser support is very limited (Jan, 2021). The example code works well in Chrome Desktop 86.
Just listen to the click event as well.
Following from Shiboe's example, here's a jQuery example:
var godzilla = $('#godzilla');
var godzillaBtn = $('#godzilla-btn');
godzillaBtn.on('click', function(){
godzilla.trigger('click');
});
godzilla.on('change click', function(){
if (godzilla.val() != '') {
$('#state').html('You have chosen a Mech!');
} else {
$('#state').html('Choose your Mech!');
}
});
You can see it in action here: http://jsfiddle.net/T3Vwz
You can catch the cancel if you choose the same file as previously and you click cancel: in this case.
You can do it like this:
<input type="file" id="myinputfile"/>
<script>
document.getElementById('myinputfile').addEventListener('change', myMethod, false);
function myMethod(evt) {
var files = evt.target.files;
f= files[0];
if (f==undefined) {
// the user has clicked on cancel
}
else if (f.name.match(".*\.jpg")|| f.name.match(".*\.png")) {
//.... the user has choosen an image file
var reader = new FileReader();
reader.onload = function(evt) {
try {
myimage.src=evt.target.result;
...
} catch (err) {
...
}
};
}
reader.readAsDataURL(f);
</script>
The easiest way is to check if there are any files in temporary memory. If you want to get the change event every time user clicks the file input you can trigger it.
var yourFileInput = $("#yourFileInput");
yourFileInput.on('mouseup', function() {
$(this).trigger("change");
}).on('change', function() {
if (this.files.length) {
//User chose a picture
} else {
//User clicked cancel
}
});
In my case i had to hide submit button while users were selecting images.
This is what i come up:
$(document).on('click', '#image-field', function(e) {
$('.submit-button').prop('disabled', true)
})
$(document).on('focus', '#image-field'), function(e) {
$('.submit-button').prop('disabled', false)
})
#image-field is my file selector. When somenone clicks on it, i disable the form submit button. The point is, when the file dialog closed - doesn't matter they select a file or cancel - #image-field got the focus back, so i listen on that event.
UPDATE
I found that, this does not work in safari and poltergeist/phantomjs. Take this info into account if you would like to implement it.
Shiboe's solution would be a good one if it worked on mobile webkit, but it doesn't. What I can come up with is to add a mousemove event listener to some dom object at the time that the file input window is opened, like so:
$('.upload-progress').mousemove(function() {
checkForFiles(this);
});
checkForFiles = function(me) {
var filefield = $('#myfileinput');
var files = filefield.get(0).files;
if (files == undefined || files[0] == undefined) $(me).remove(); // user cancelled the upload
};
The mousemove event is blocked from the page while the file dialog is open, and when its closed one checks to see if there are any files in the file input. In my case I want an activity indicator blocking things till the file is uploaded, so I only want to remove my indicator on cancel.
However this doesn't solve for mobile, since there is no mouse to move. My solution there is less than perfect, but I think its good enough.
$('.upload-progress').bind('touchstart', function() {
checkForFiles(this);
});
Now we're listening for a touch on the screen to do the same files check. I'm pretty confident that the user's finger will be put on the screen pretty quickly after cancel and dismiss this activity indicator.
One could also just add the activity indicator on the file input change event, but on mobile there is often a few seconds lag between selecting the image and the change event firing, so its just much better UX for the activity indicator to be displayed at the start of the process.
I found this atribute, its most simple yet.
if ($('#selectedFile')[0].files.length > 1)
{
// Clicked on 'open' with file
} else {
// Clicked on 'cancel'
}
Here, selectedFile is an input type=file.
I know this is a very old question but just in case it helps someone, I found when using the onmousemove event to detect the cancel, that it was necessary to test for two or more such events in a short space of time.
This was because single onmousemove events are generated by the browser (Chrome 65) each time the cursor is moved out of the select file dialog window and each time it is moved out of the main window and back in.
A simple counter of mouse movement events coupled with a short duration timeout to reset the counter back to zero worked a treat.
Combining Shiboe's and alx's solutions, i've got the most reliable code:
var selector = $('<input/>')
.attr({ /* just for example, use your own attributes */
"id": "FilesSelector",
"name": "File",
"type": "file",
"contentEditable": "false" /* if you "click" on input via label, this prevents IE7-8 from just setting caret into file input's text filed*/
})
.on("click.filesSelector", function () {
/* do some magic here, e.g. invoke callback for selection begin */
var cancelled = false; /* need this because .one calls handler once for each event type */
setTimeout(function () {
$(document).one("mousemove.filesSelector focusin.filesSelector", function () {
/* namespace is optional */
if (selector.val().length === 0 && !cancelled) {
cancelled = true; /* prevent double cancel */
/* that's the point of cancel, */
}
});
}, 1); /* 1 is enough as we just need to delay until first available tick */
})
.on("change.filesSelector", function () {
/* do some magic here, e.g. invoke callback for successful selection */
})
.appendTo(yourHolder).end(); /* just for example */
Generally, mousemove event does the trick, but in case user made a click and than:
cancelled file open dialog by escape key (without moving a mouse), made another accurate click to open file dialog again...
switched focus to any other application, than came back to browser's file open dialog and closed it, than opened again via enter or space key...
... we won't get mousemove event hence no cancel callback. Moreover, if user cancels second dialog and makes a mouse move, we'll get 2 cancel callbacks.
Fortunately, special jQuery focusIn event bubbles up to the document in both cases, helping us to avoid such situations. The only limitation is if one blocks focusIn event either.
I see that my response would be quite outdated, but never the less.
I faced with the same problem. So here's my solution.
The most useful code snipped was KGA's one. But it isn't totally working and is a bit complicated. But I simplified it.
Also, the main trouble maker was that fact, that 'change' event doesn't come instantly after focus, so we have to wait for some time.
"#appendfile" - which user clicks on to append a new file.
Hrefs get focus events.
$("#appendfile").one("focusin", function () {
// no matter - user uploaded file or canceled,
// appendfile gets focus
// change doesn't come instantly after focus, so we have to wait for some time
// wrapper represents an element where a new file input is placed into
setTimeout(function(){
if (wrapper.find("input.fileinput").val() != "") {
// user has uploaded some file
// add your logic for new file here
}
else {
// user canceled file upload
// you have to remove a fileinput element from DOM
}
}, 900);
});
You can detect this only in limited circumstances. Specifically, in chrome if a file was selected earlier and then the file dialog is clicked and cancel clicked, Chrome clears the file and fires the onChange event.
https://code.google.com/p/chromium/issues/detail?id=2508
In this scenario, you can detect this by handling the onChange event and checking the files property.
This is hacky at best, but here is a working example of my solution to detect whether or not a user has uploaded a file, and only allowing them to proceed if they have uploaded a file.
Basically hide the Continue, Save, Proceed or whatever your button is. Then in the JavaScript you grab the file name. If the file name does not have a value, then do not show the Continue button. If it does have a value, then show the button. This also works if they at first upload a file and then they try to upload a different file and click cancel.
Here is the code.
HTML:
<div class="container">
<div class="row">
<input class="file-input" type="file" accept="image/*" name="fileUpload" id="fileUpload" capture="camera">
<label for="fileUpload" id="file-upload-btn">Capture or Upload Photo</label>
</div>
<div class="row padding-top-two-em">
<input class="btn btn-success hidden" id="accept-btn" type="submit" value="Accept & Continue"/>
<button class="btn btn-danger">Back</button>
</div></div>
JavaScript:
$('#fileUpload').change(function () {
var fileName = $('#fileUpload').val();
if (fileName != "") {
$('#file-upload-btn').html(fileName);
$('#accept-btn').removeClass('hidden').addClass('show');
} else {
$('#file-upload-btn').html("Upload File");
$('#accept-btn').addClass('hidden');
}
});
CSS:
.file-input {
width: 0.1px;
height: 0.1px;
opacity: 0;
overflow: hidden;
position: absolute;
z-index: -1;
}
.file-input + label {
font-size: 1.25em;
font-weight: normal;
color: white;
background-color: blue;
display: inline-block;
padding: 5px;
}
.file-input:focus + label,
.file-input + label:hover {
background-color: red;
}
.file-input + label {
cursor: pointer;
}
.file-input + label * {
pointer-events: none;
}
For the CSS a lot of this is to make the website and button accessible for everyone. Style your button to whatever you like.
The following seems to work for me (on desktop, windows):
var openFile = function (mimeType, fileExtension) {
var defer = $q.defer();
var uploadInput = document.createElement("input");
uploadInput.type = 'file';
uploadInput.accept = '.' + fileExtension + ',' + mimeType;
var hasActivated = false;
var hasChangedBeenCalled = false;
var hasFocusBeenCalled = false;
var focusCallback = function () {
if (hasActivated) {
hasFocusBeenCalled = true;
document.removeEventListener('focus', focusCallback, true);
setTimeout(function () {
if (!hasChangedBeenCalled) {
uploadInput.removeEventListener('change', changedCallback, true);
defer.resolve(null);
}
}, 300);
}
};
var changedCallback = function () {
uploadInput.removeEventListener('change', changedCallback, true);
if (!hasFocusBeenCalled) {
document.removeEventListener('focus', focusCallback, true);
}
hasChangedBeenCalled = true;
if (uploadInput.files.length === 1) {
//File picked
var reader = new FileReader();
reader.onload = function (e) {
defer.resolve(e.target.result);
};
reader.readAsText(uploadInput.files[0]);
}
else {
defer.resolve(null);
}
};
document.addEventListener('focus', focusCallback, true); //Detect cancel
uploadInput.addEventListener('change', changedCallback, true); //Detect when a file is picked
uploadInput.click();
hasActivated = true;
return defer.promise;
}
This does use angularjs $q but you should be able to replace it with any other promise framework if needed.
Tested on IE11, Edge, Chrome, Firefox, but it does not seem to work on Chrome on a Android Tablet as it does not fire the Focus event.
The file-type field, frustratingly, doesn't respond to a lot of events (blur would be lovely). I see a lot of people suggesting change-oriented solutions and them getting downvoted.
change does work, but it has a major flaw (vs what we want to happen).
When you freshly load a page containing a file field, open the box and press cancel. Nothing, frustratingly, changes.
What I chose to do is load in a gated-state.
The next part of the form a section#after-image in my case is hidden from view. When my file field changes, an upload button is shown. Upon successful upload, section#after-image is shown.
If the user loads, opens the file-dialog, then cancels out, they never see the upload button.
If the user chooses a file, the upload button is shown. If they then open the dialog and cancel, the change event is triggered by this cancel, and there I can (and do) re-hide my upload button until a proper file is selected.
I was fortunate that this gated-state was already the design of my form. You do not need to use the same style, merely having the upload button initially hidden and upon change, setting a hidden field or javascript variable to something you can monitor on submit.
I tried changing the value of files[0] before the field was interacted with. This didn't do anything regarding onchange.
So yes, change works, at least as good as we're going to get. The filefield is secured, for obvious reasons, but to the frustration of well-intentioned developers.
It's not fitting to my purpose, but you might be able to, onclick, load a warning prompt (not an alert(), because that stalls page-processing), and hide it if change is triggered and files[0] is null. If change is not triggered, the div remains in its state.
Solution for file selection with hidden input
Note: this code doesn't detect cancellation, it offers a way to circumvent the need to detect it in a common case in which people try to detect it.
I got here while looking for a solution for file uploads using a hidden input, I believe that this is the most common reason to look for a way to detect cancellation of file input (open file dialog -> if a file was selected then run some code, otherwise do nothing), here's my solution:
var fileSelectorResolve;
var fileSelector = document.createElement('input');
fileSelector.setAttribute('type', 'file');
fileSelector.addEventListener('input', function(){
fileSelectorResolve(this.files[0]);
fileSelectorResolve = null;
fileSelector.value = '';
});
function selectFile(){
if(fileSelectorResolve){
fileSelectorResolve();
fileSelectorResolve = null;
}
return new Promise(function(resolve){
fileSelectorResolve = resolve;
fileSelector.dispatchEvent(new MouseEvent('click'));
});
}
Usage example:
Note that if no file was selected then the first line will return only once selectFile() is called again (or if you called fileSelectorResolve() from elsewhere).
async function logFileName(){
const file = await selectFile();
if(!file) return;
console.log(file.name);
}
Another example:
async function uploadFile(){
const file = await selectFile();
if(!file) return;
// ... make an ajax call here to upload the file ...
}
There is a hackish way to do this (add callbacks or resolve some deferred/promise implementation instead of alert() calls):
var result = null;
$('<input type="file" />')
.on('change', function () {
result = this.files[0];
alert('selected!');
})
.click();
setTimeout(function () {
$(document).one('mousemove', function () {
if (!result) {
alert('cancelled');
}
});
}, 1000);
How it works: while file selection dialog is open, document does not receive mouse pointer events. There is 1000ms delay to allow the dialog to actually appear and block browser window. Checked in Chrome and Firefox (Windows only).
But this is not a reliable way to detect cancelled dialog, of course. Though, might improve some UI behavior for you.
Here is my solution, using the file input focus (not using any timers)
var fileInputSelectionInitiated = false;
function fileInputAnimationStart() {
fileInputSelectionInitiated = true;
if (!$("#image-selector-area-icon").hasClass("fa-spin"))
$("#image-selector-area-icon").addClass("fa-spin");
if (!$("#image-selector-button-icon").hasClass("fa-spin"))
$("#image-selector-button-icon").addClass("fa-spin");
}
function fileInputAnimationStop() {
fileInputSelectionInitiated = false;
if ($("#image-selector-area-icon").hasClass("fa-spin"))
$("#image-selector-area-icon").removeClass("fa-spin");
if ($("#image-selector-button-icon").hasClass("fa-spin"))
$("#image-selector-button-icon").removeClass("fa-spin");
}
$("#image-selector-area-wrapper").click(function (e) {
$("#fileinput").focus();
$("#fileinput").click();
});
$("#preview-image-wrapper").click(function (e) {
$("#fileinput").focus();
$("#fileinput").click();
});
$("#fileinput").click(function (e) {
fileInputAnimationStart();
});
$("#fileinput").focus(function (e) {
fileInputAnimationStop();
});
$("#fileinput").change(function(e) {
// ...
}
Well, this doesn't exactly answers your question. My assumption is that, you have a scenario, when you add a file input, and invoke file selection, and if user hits cancel, you just remove the input.
If this is the case, then: Why adding empty file input?
Create the one on the fly, but add it to DOM only when it is filled in. Like so:
var fileInput = $("<input type='file' name='files' style='display: none' />");
fileInput.bind("change", function() {
if (fileInput.val() !== null) {
// if has value add it to DOM
$("#files").append(fileInput);
}
}).click();
So here I create <input type="file" /> on the fly, bind to it's change event and then immediately invoke click. On change will fire only when user selects a file and hits Ok, otherwise input will not be added to DOM, therefore will not be submitted.
Working example here: https://jsfiddle.net/69g0Lxno/3/
//Use hover instead of blur
var fileInput = $("#fileInput");
if (fileInput.is(":hover") {
//open
} else {
}
function file_click() {
document.body.onfocus = () => {
setTimeout(_=>{
let file_input = document.getElementById('file_input');
if (!file_input.value) alert('please choose file ')
else alert(file_input.value)
document.body.onfocus = null
},100)
}
}
Using setTimeout to get the certain value of the input.
If you already require JQuery, this solution might do the work (this is the exact same code I actually needed in my case, although using a Promise is just to force the code to wait until file selection has been resolved):
await new Promise(resolve => {
const input = $("<input type='file'/>");
input.on('change', function() {
resolve($(this).val());
});
$('body').one('focus', '*', e => {
resolve(null);
e.stopPropagation();
});
input.click();
});
There are several proposed solutions in this thread and this difficulty to detecting when the user clicks the "Cancel" button on the file selection box is a problem that affects many people.
The fact is that there is no 100% reliable way to detect if the user has clicked the "Cancel" button on the file selection box. But there are ways to reliably detect if the user has added a file to the input file. So this is the basic strategy of this answer!
I decided to add this answer because apparently the other answers don't work on most browsers or guaranteed on mobile devices.
Briefly the code is based on 3 points:
The input file is initially created dynamically in "memory" in js
(we don't add it to the "HTML" at this moment);
After adding the file then the input file is added to the HTML, otherwise nothing occurs;
The removal of the file is done by removing the input file from the
HTML by a specific event, which means that the
"editing"/"modification" of the file is done by removing the old
input file and creating a new one.
For a better understanding look at the code below and the notes as well.
[...]
<button type="button" onclick="addIptFl();">ADD INPUT FILE!</button>
<span id="ipt_fl_parent"></span>
[...]
function dynIptFl(jqElInst, funcsObj) {
if (typeof funcsObj === "undefined" || funcsObj === "") {
funcsObj = {};
}
if (funcsObj.hasOwnProperty("before")) {
if (!funcsObj["before"].hasOwnProperty("args")) {
funcsObj["before"]["args"] = [];
}
funcsObj["before"]["func"].apply(this, funcsObj["before"]["args"]);
}
var jqElInstFl = jqElInst.find("input[type=file]");
// NOTE: Open the file selection box via js. By Questor
jqElInstFl.trigger("click");
// NOTE: This event is triggered if the user selects a file. By Questor
jqElInstFl.on("change", {funcsObj: funcsObj}, function(e) {
// NOTE: With the strategy below we avoid problems with other unwanted events
// that may be associated with the DOM element. By Questor
e.preventDefault();
var funcsObj = e.data.funcsObj;
if (funcsObj.hasOwnProperty("after")) {
if (!funcsObj["after"].hasOwnProperty("args")) {
funcsObj["after"]["args"] = [];
}
funcsObj["after"]["func"].apply(this, funcsObj["after"]["args"]);
}
});
}
function remIptFl() {
// NOTE: Remove the input file. By Questor
$("#ipt_fl_parent").empty();
}
function addIptFl() {
function addBefore(someArgs0, someArgs1) {
// NOTE: All the logic here happens just before the file selection box opens.
// By Questor
// SOME CODE HERE!
}
function addAfter(someArgs0, someArgs1) {
// NOTE: All the logic here happens only if the user adds a file. By Questor
// SOME CODE HERE!
$("#ipt_fl_parent").prepend(jqElInst);
}
// NOTE: The input file is hidden as all manipulation must be done via js.
// By Questor
var jqElInst = $('\
<span>\
<button type="button" onclick="remIptFl();">REMOVE INPUT FILE!</button>\
<input type="file" name="input_fl_nm" style="display: block;">\
</span>\
');
var funcsObj = {
before: {
func: addBefore,
args: [someArgs0, someArgs1]
},
after: {
func: addAfter,
// NOTE: The instance with the input file ("jqElInst") could be passed
// here instead of using the context of the "addIptFl()" function. That
// way "addBefore()" and "addAfter()" will not need to be inside "addIptFl()",
// for example. By Questor
args: [someArgs0, someArgs1]
}
};
dynIptFl(jqElInst, funcsObj);
}
Thanks! =D
We achieved in angular like below.
bind click event on input type file.
Attach focus event with window and add condition if uploadPanel is true then show console.
when click on input type file the boolean uploadPanel value is true. and dialogue box appear.
when cancel OR Esc button click then dialogue box dispensary and console appear.
HTML
<input type="file" formControlName="FileUpload" click)="handleFileInput($event.target.files)" />
/>
TS
this.uploadPanel = false;
handleFileInput(files: FileList) {
this.fileToUpload = files.item(0);
console.log("ggg" + files);
this.uploadPanel = true;
}
#HostListener("window:focus", ["$event"])
onFocus(event: FocusEvent): void {
if (this.uploadPanel == true) {
console.log("cancel clicked")
this.addSlot
.get("FileUpload")
.setValidators([
Validators.required,
FileValidator.validate,
requiredFileType("png")
]);
this.addSlot.get("FileUpload").updateValueAndValidity();
}
}
Just add 'change' listener on your input whose type is file. i.e
<input type="file" id="file_to_upload" name="file_to_upload" />
I have done using jQuery and obviously anyone can use valina JS (as per the requirement).
$("#file_to_upload").change(function() {
if (this.files.length) {
alert('file choosen');
} else {
alert('file NOT choosen');
}
});

Firing a manual click event on an button in ember.js doesn't give the required result

TL;DR: Trying to fire a manual javascript click event on the chat button of twitch, won't send the message. Don't understand why the event doesn't do the same as a normal click and don't know how to make it work.
So I am trying to make a custom bot for twitch.tv, only reading his info from the HTML directly. I've got it perfectly working up to the point at where it can recognize commands and put text in the textbox. Now the problem I have is, as soon as I try to fire a manual click event on the "chat" button, it just doesn't seem to work. My guess is it has something to do with ember.js, and I frankly don't know anything about that. Anyway, here is the part of the code that doesn't work. EDIT: this works if I enter it as single in the console, doesn't work in context of the rest of my code though.
$('.send-chat-button').click();
What happens here is that I acquire a piece of html that contains the chat submit button, which is this:
<button class="button primary float-right send-chat-button" data-bindattr-3945="3945">
<span>
Chat
</span>
</button>
When I try to manually fire a click event on this, nothing happens. However, when I fire a manual click event on buttonContain.children[0] and buttonContain.children1 (which are, respectively, the settings and list of viewers buttons), it does work. They look like this:
<a data-ember-action="3943" class="button glyph-only float-left" title="Chat Settings"></a>
I'm guessing the difference is in the data-ember-action and the data-bindattr-*, but I don't know how to make it work. Anyone here knows why the click() event doesn't work and directly clicking does?
EDIT: If you have any questions about my question, feel free to ask.
EDIT2: I experimented a little more, and I can remove all HTML attributes from the button, and clicking on it will still work. I have no idea what is going on :(.
EDIT3: Okay, so it seems it only stops working when i remove the
Span within the button
Still no idea what is going on. (Yes, have also tried to fire the click event on the span)
EDIT4: As requested, here is all the code from my side. Note that I'm trying to click a button from twitch itself, of which ember side I do not own any code. This code is used by pasting it in the console on a twitch.tv stream and then starting it by calling initiateMessageProcessing. I'm sorry for the lot of hardcoded values, those are twitch' fields that I need. For now I'm just looking for a proof of concept.
var frequency = 5000;
var myInterval = 0;
var lastMessageId = 0;
function initiateMessageProcessing() {
if (myInterval > 0) {
clearInterval(myInterval);
}
myInterval = setInterval("checkMessages()", frequency);
}
function checkMessages() {
var chat = document.getElementsByClassName("chat-lines")[0];
processMessages(extractUnprocessedMessages(chat.children));
lastMessageId = parseInt(chat.lastElementChild.getAttribute("id").substring(5, 10));
}
function extractUnprocessedMessages(chat) {
var unprocessedMessages = [];
var chatId = 0;
for ( i = 0; i < chat.length; i++) {
chatId = parseInt(chat[i].getAttribute("id").substring(5, 10));
if (chatId > lastMessageId) {
unprocessedMessages.push(chat[i]);
}
}
return unprocessedMessages;
}
function processMessages(unprocessedMessages) {
var messageElement;
for ( i = 0; i < unprocessedMessages.length; i++) {
messageElement = unprocessedMessages[i].children[0].getElementsByClassName("message")[0];
if (messageElement != undefined && messageElement != null) {
if (messageElement.innerHTML.search("!test") !== -1) {
sendMessage('Hello world!');
}
}
}
}
function sendMessage(message) {
fillTextArea(message);
var button = $('.send-chat-button').get(0);
var event = new MouseEvent('click', {
bubbles : true
});
button.dispatchEvent(event);
}
function fillTextArea(message){
var textArea;
var chatInterface = document.getElementsByClassName("chat-interface")[0];
var textAreaContain = chatInterface.children[0];
textArea = textAreaContain.children[0].children[0];
textArea.value = message;
}
EDIT5: Eventlistener screenshot:
EDIT6: Edited source code to use $('.send-chat-button').click();
I have tried this, does not work in the current code, it does work if I manually fire this single command in the console when there is text in the chat. But sadly does not work in my code.
EDIT7: used Ember.run, still doesn't work.
EDIT8: used dispatchmouseevent, still doesn't work in context of code
It seems that the target site attaches event listeners without help of JQuery. If it is so, you cannot trigger it using jquery .click() method.
You can try directly mocking the browser event like this:
var button = $('.send-chat-button').get(0);
var event = new MouseEvent('click', {bubbles: true});
button.dispatchEvent(event);
This code will not work in IE8 and lower, but I guess it is not your case.
I know this post is quite old but I had been looking for an answer on this for a while and nothing really worked, after trying out A LOT of stuff I found it works when you focus the chatbox first then focus the button then triggering the click event!!! uuuhm yeah...
$('.chat_text_input').focus();
$('.send-chat-button').focus().trigger('click');
I have no idea why this works (and why it doesn't in any other way), but leaving any of the focusses out makes it fail or bug out.
Programmatically clicking a DOM element to make some action done is somewhat a wrong approach.
You should have define a method myAction() which will be called in two ways. First, from your ember action triggerMyAction() and second, after listening to a custom event, "myEvent".
Instead of $('.send-chat-button').click(); you will code $('something').trigger("myEvent") then.
Something like:
Em.Controller.extend({
myAction:function(){
//do your stuff
},
onMyEvent:function(){
$('something').on('myEvent',this.myAction);
}.on('didInsertElement'),
actions:{
triggerMyAction:function(){
this.myAction();
}
}
})

TinyMCE opened in jqueryUI modal dialog

When using tinyMCE in a jqueryUI modal dialog, I can't use the hyperlink or 'insert image' features.
Basically, after lots of searching, I've found this:
http://www.tinymce.com/develop/bugtracker_view.php?id=5917
The weird thing is that to me it seams less of a tinyMCE issue and more of a jqueryUI issue since the problem is not present when jqueryUI's modal property is set to false.
With a richer form I saw that what happens is that whenever the tinyMCE loses focus, the first element in the form gets focus even if it's not the one focused / clicked.
Does some JavaScript guru have any idea how I might be able to keep the dialog modal and make tinyMCE work?
This fixed it for me when overriding _allowInteraction would not:
$(document).on('focusin', function(e) {
if ($(event.target).closest(".mce-window").length) {
e.stopImmediatePropagation();
}
});
I can't really take credit for it. I got it from this thread on the TinyMCE forums.
(They have moved their bugtracker to github. tinymce/issues/703 is the corresponding github issue.)
It seems there are no propper solution for this issue yet. This is kind of a hack but it really worked for me.
Every time you open the Dialog remove the text area and re add it like following,
var myDialog = $('#myDialog');
var myTextarea = myDialog.find('textarea');
var clonedTextArea = myTextarea.clone(); // create a copy before deleting from the DOM
var myTextAreaParent = myTextarea.parent(); // get the parent to add the created copy later
myTextarea.remove(); // remove the textarea
myDialog.find('.mce-container').remove(); // remove existing mce control if exists
myTextAreaParent.append(clonedTextArea); // re-add the copy
myDialog.dialog({
open: function(e1,e2){
setTimeout(function () {
// Add your tinymce creation code here
},50);
}
});
myDialog.dialog('open');
This seems to fix it for me, or at least work around it (put it somewhere in your $(document).ready()):
$.widget('ui.dialog', $.ui.dialog, {
_allowInteraction: function(event) {
return ($('.mce-panel:visible').length > 0);
}
});

Clearing <input type='file' /> using jQuery

Is it possible to clear an <input type='file' /> control value with jQuery? I've tried the following:
$('#control').attr({ value: '' });
But it's not working.
Easy: you wrap a <form> around the element, call reset on the form, then remove the form using .unwrap(). Unlike the .clone() solutions otherwise in this thread, you end up with the same element at the end (including custom properties that were set on it).
Tested and working in Opera, Firefox, Safari, Chrome and IE6+. Also works on other types of form elements, with the exception of type="hidden".
window.reset = function(e) {
e.wrap('<form>').closest('form').get(0).reset();
e.unwrap();
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form>
<input id="file" type="file">
<br>
<input id="text" type="text" value="Original">
</form>
<button onclick="reset($('#file'))">Reset file</button>
<button onclick="reset($('#text'))">Reset text</button>
JSFiddle
As Timo notes below, if you have the buttons to trigger the reset of the field inside of the <form>, you must call .preventDefault() on the event to prevent the <button> from triggering a submit.
EDIT
Does not work in IE 11 due to an unfixed bug. The text (file name) is cleared on the input, but its File list remains populated.
Quick answer: replace it.
In the code below I use the replaceWith jQuery method to replace the control with a clone of itself. In the event you have any handlers bound to events on this control, we'll want to preserve those as well. To do this we pass in true as the first parameter of the clone method.
<input type="file" id="control"/>
<button id="clear">Clear</button>
var control = $("#control");
$("#clear").on("click", function () {
control.replaceWith( control = control.clone( true ) );
});
Fiddle: http://jsfiddle.net/jonathansampson/dAQVM/
If cloning, while preserving event handlers, presents any issues you could consider using event delegation to handle clicks on this control from a parent element:
$("form").on("focus", "#control", doStuff);
This prevents the need for any handlers to be cloned along with the element when the control is being refreshed.
Jquery is supposed to take care of the cross-browser/older browser issues for you.
This works on modern browsers that I tested: Chromium v25, Firefox v20, Opera v12.14
Using jquery 1.9.1
HTML
<input id="fileopen" type="file" value="" />
<button id="clear">Clear</button>
Jquery
$("#clear").click(function () {
$("#fileopen").val("");
});
On jsfiddle
The following javascript solution also worked for me on the browsers mention above.
document.getElementById("clear").addEventListener("click", function () {
document.getElementById("fileopen").value = "";
}, false);
On jsfiddle
I have no way to test with IE, but theoretically this should work. If IE is different enough that the Javascript version does not work because MS have done it in a different way, the jquery method should in my opinion deal with it for you, else it would be worth pointing it out to the jquery team along with the method that IE requires. (I see people saying "this won't work on IE", but no vanilla javascript to show how it does work on IE (supposedly a "security feature"?), perhaps report it as a bug to MS too (if they would count it as such), so that it gets fixed in any newer release)
Like mentioned in another answer, a post on the jquery forum
if ($.browser.msie) {
$('#file').replaceWith($('#file').clone());
} else {
$('#file').val('');
}
But jquery have now removed support for browser testing, jquery.browser.
This javascript solution also worked for me, it is the vanilla equivalent of the jquery.replaceWith method.
document.getElementById("clear").addEventListener("click", function () {
var fileopen = document.getElementById("fileopen"),
clone = fileopen.cloneNode(true);
fileopen.parentNode.replaceChild(clone, fileopen);
}, false);
On jsfiddle
The important thing to note is that the cloneNode method does not preserve associated event handlers.
See this example.
document.getElementById("fileopen").addEventListener("change", function () {
alert("change");
}, false);
document.getElementById("clear").addEventListener("click", function () {
var fileopen = document.getElementById("fileopen"),
clone = fileopen.cloneNode(true);
fileopen.parentNode.replaceChild(clone, fileopen);
}, false);
On jsfiddle
But jquery.clone offers this [*1]
$("#fileopen").change(function () {
alert("change");
});
$("#clear").click(function () {
var fileopen = $("#fileopen"),
clone = fileopen.clone(true);
fileopen.replaceWith(clone);
});
On jsfiddle
[*1] jquery is able to do this if the events were added by jquery's methods as it keeps a copy in jquery.data, it does not work otherwise, so it's a bit of a cheat/work-around and means things are not compatible between different methods or libraries.
document.getElementById("fileopen").addEventListener("change", function () {
alert("change");
}, false);
$("#clear").click(function () {
var fileopen = $("#fileopen"),
clone = fileopen.clone(true);
fileopen.replaceWith(clone);
});
On jsfiddle
You can not get the attached event handler direct from the element itself.
Here is the general principle in vanilla javascript, this is how jquery an all other libraries do it (roughly).
(function () {
var listeners = [];
function getListeners(node) {
var length = listeners.length,
i = 0,
result = [],
listener;
while (i < length) {
listener = listeners[i];
if (listener.node === node) {
result.push(listener);
}
i += 1;
}
return result;
}
function addEventListener(node, type, handler) {
listeners.push({
"node": node,
"type": type,
"handler": handler
});
node.addEventListener(type, handler, false);
}
function cloneNode(node, deep, withEvents) {
var clone = node.cloneNode(deep),
attached,
length,
evt,
i = 0;
if (withEvents) {
attached = getListeners(node);
if (attached) {
length = attached.length;
while (i < length) {
evt = attached[i];
addEventListener(clone, evt.type, evt.handler);
i += 1;
}
}
}
return clone;
}
addEventListener(document.getElementById("fileopen"), "change", function () {
alert("change");
});
addEventListener(document.getElementById("clear"), "click", function () {
var fileopen = document.getElementById("fileopen"),
clone = cloneNode(fileopen, true, true);
fileopen.parentNode.replaceChild(clone, fileopen);
});
}());
On jsfiddle
Of course jquery and other libraries have all the other support methods required for maintaining such a list, this is just a demonstration.
For obvious security reasons you can't set the value of a file input, even to an empty string.
All you have to do is reset the form where the field or if you only want to reset the file input of a form containing other fields, use this:
function reset_field (e) {
e.wrap('<form>').parent('form').trigger('reset');
e.unwrap();
}​
Here is an exemple: http://jsfiddle.net/v2SZJ/1/
This works for me.
$("#file").replaceWith($("#file").clone());
http://forum.jquery.com/topic/how-to-clear-a-file-input-in-ie
Hope it helps.
In IE8 they made the File Upload field read-only for security. See the IE team blog post:
Historically, the HTML File Upload Control () has been the source of a significant number of information disclosure vulnerabilities. To resolve these issues, two changes were made to the behavior of the control.
To block attacks that rely on “stealing” keystrokes to surreptitiously trick the user into typing a local file path into the control, the File Path edit box is now read-only. The user must explicitly select a file for upload using the File Browse dialog.
Additionally, the “Include local directory path when uploading files” URLAction has been set to "Disable" for the Internet Zone. This change prevents leakage of potentially sensitive local file-system information to the Internet. For instance, rather than submitting the full path C:\users\ericlaw\documents\secret\image.png, Internet Explorer 8 will now submit only the filename image.png.
$("#control").val('') is all you need! Tested on Chrome using JQuery 1.11
Other users have tested in Firefox as well.
I got stuck with all the options here. Here's a hack that I made which worked:
<form>
<input type="file">
<button type="reset" id="file_reset" style="display:none">
</form>
and you can trigger the reset using jQuery with a code similar to this:
$('#file_reset').trigger('click');
(jsfiddle: http://jsfiddle.net/eCbd6/)
I ended up with this:
if($.browser.msie || $.browser.webkit){
// doesn't work with opera and FF
$(this).after($(this).clone(true)).remove();
}else{
this.setAttribute('type', 'text');
this.setAttribute('type', 'file');
}
may not be the most elegant solution, but it work as far as I can tell.
I have used https://github.com/malsup/form/blob/master/jquery.form.js, which has a function called clearInputs(), which is crossbrowser, well tested, easy to use and handles also IE issue and hidden fields clearing if needed. Maybe a little long solution to only clear file input, but if you are dealing with crossbrowser file uploads, then this solution is recommended.
The usage is easy:
// Clear all file fields:
$("input:file").clearInputs();
// Clear also hidden fields:
$("input:file").clearInputs(true);
// Clear specific fields:
$("#myfilefield1,#myfilefield2").clearInputs();
/**
* Clears the selected form elements.
*/
$.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
return this.each(function() {
var t = this.type, tag = this.tagName.toLowerCase();
if (re.test(t) || tag == 'textarea') {
this.value = '';
}
else if (t == 'checkbox' || t == 'radio') {
this.checked = false;
}
else if (tag == 'select') {
this.selectedIndex = -1;
}
else if (t == "file") {
if (/MSIE/.test(navigator.userAgent)) {
$(this).replaceWith($(this).clone(true));
} else {
$(this).val('');
}
}
else if (includeHidden) {
// includeHidden can be the value true, or it can be a selector string
// indicating a special test; for example:
// $('#myForm').clearForm('.special:hidden')
// the above would clean hidden inputs that have the class of 'special'
if ( (includeHidden === true && /hidden/.test(t)) ||
(typeof includeHidden == 'string' && $(this).is(includeHidden)) )
this.value = '';
}
});
};
The value of file inputs is read only (for security reasons). You can't blank it programatically (other than by calling the reset() method of the form, which has a broader scope than just that field).
I was able to get mine working with the following code:
var input = $("#control");
input.replaceWith(input.val('').clone(true));
I have been looking for simple and clean way to clear HTML file input, the above answers are great, but none of them really answers what i'm looking for, until i came across on the web with simple an elegant way to do it :
var $input = $("#control");
$input.replaceWith($input.val('').clone(true));
all the credit go's to Chris Coyier.
// Referneces
var control = $("#control"),
clearBn = $("#clear");
// Setup the clear functionality
clearBn.on("click", function(){
control.replaceWith( control.val('').clone( true ) );
});
// Some bound handlers to preserve when cloning
control.on({
change: function(){ console.log( "Changed" ) },
focus: function(){ console.log( "Focus" ) }
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="file" id="control">
<br><br>
Clear
The .clone() thing does not work in Opera (and possibly others). It keeps the content.
The closest method here for me was Jonathan's earlier, however ensuring that the field preserved its name, classes, etc made for messy code in my case.
Something like this might work well (thanks to Quentin too):
function clearInput($source) {
var $form = $('<form>')
var $targ = $source.clone().appendTo($form)
$form[0].reset()
$source.replaceWith($targ)
}
I have managed to get this to work using the following...
function resetFileElement(ele)
{
ele.val('');
ele.wrap('<form>').parent('form').trigger('reset');
ele.unwrap();
ele.prop('files')[0] = null;
ele.replaceWith(ele.clone());
}
This has been tested in IE10, FF, Chrome & Opera.
There are two caveats...
Still doesn't work properly in FF, if you refresh the page, the file element gets re-populated with the selected file. Where it is getting this info from is beyond me. What else related to a file input element could I possible try to clear?
Remember to use delegation on any events you had attached to the file input element, so they still work when the clone is made.
What I don't understand is who on earth thought not allowing you to clear an input field from an invalid unacceptable file selection was a good idea?
OK, don't let me dynamically set it with a value so I can't leach files from a user's OS, but let me clear an invalid selection without resetting an entire form.
It's not like 'accept' does anything other than a filter anyhow and in IE10, it doesn't even understand MS Word mime types, it's a joke!
On my Firefox 40.0.3 only work with this
$('input[type=file]').val('');
$('input[type=file]').replaceWith($('input[type=file]').clone(true));
its works for me in every browser.
var input = $(this);
var next = this.nextSibling;
var parent = input.parent();
var form = $("<form></form>");
form.append(input);
form[0].reset();
if (next) {
$(next).before(input);
} else {
parent.append(input);
}
I tried with the most of the techniques the users mentioned, but none of they worked in all browsers. i.e: clone() doesn't work in FF for file inputs.
I ended up copying manually the file input, and then replacing the original with the copied one. It works in all browsers.
<input type="file" id="fileID" class="aClass" name="aName"/>
var $fileInput=$("#fileID");
var $fileCopy=$("<input type='file' class='"+$fileInput.attr("class")+" id='fileID' name='"+$fileInput.attr("name")+"'/>");
$fileInput.replaceWith($fileCopy);
$("input[type=file]").wrap("<div id='fileWrapper'/>");
$("#fileWrapper").append("<div id='duplicateFile' style='display:none'>"+$("#fileWrapper").html()+"</div>");
$("#fileWrapper").html($("#duplicateFile").html());
This works with Chrome, FF, and Safari
$("#control").val("")
May not work with IE or Opera
Make it asynchronous, and reset it after the button's desired actions have been done.
<!-- Html Markup --->
<input id="btn" type="file" value="Button" onchange="function()" />
<script>
//Function
function function(e) {
//input your coding here
//Reset
var controlInput = $("#btn");
controlInput.replaceWith(controlInput = controlInput.val('').clone(true));
}
</script>
function clear() {
var input = document.createElement("input");
input.setAttribute('type', 'file');
input.setAttribute('value', '');
input.setAttribute('id', 'email_attach');
$('#email_attach').replaceWith( input.cloneNode() );
}
it does not work for me:
$('#Attachment').replaceWith($(this).clone());
or
$('#Attachment').replaceWith($('#Attachment').clone());
so in asp mvc I use razor features for replacing file input.
at first create a variable for input string with Id and Name and then use it for showing in page and replacing on reset button click:
#{
var attachmentInput = Html.TextBoxFor(c => c.Attachment, new { type = "file" });
}
#attachmentInput
<button type="button" onclick="$('##(Html.IdFor(p => p.Attachment))').replaceWith('#(attachmentInput)');">--</button>
An easy way is changing the input type and change it back again.
Something like this:
var input = $('#attachments');
input.prop('type', 'text');
input.prop('type', 'file')
You can replace it with its clone like so
var clone = $('#control').clone();
$('#control').replacewith(clone);
But this clones with its value too so you had better like so
var emtyValue = $('#control').val('');
var clone = emptyValue.clone();
$('#control').replacewith(clone);
It's easy lol (works in all browsers [except opera]):
$('input[type=file]').each(function(){
$(this).after($(this).clone(true)).remove();
});
JS Fiddle: http://jsfiddle.net/cw84x/1/
What?
In your validation function, just put
document.onlyform.upload.value="";
Assuming upload is the name:
<input type="file" name="upload" id="csv_doc"/>
I'm using JSP, not sure if that makes a difference...
Works for me, and I think it's way easier.

Categories