34

I have a form with some data and upload. The upload could be initiated only if data were successfully received and processed. To do that, I make an ajax call where I

  1. send data,
  2. check its result,
  3. trigger a click() to open a file dialog box.

The last thing with click() doesn't work as it seems that asynchronous call blocks opening an upload window. It works only if I set async: false.

I cannot find anything in documentation and this site and want to know what is the problem there and how to make it working keeping the call asynchronous?

Example:

$.ajax({
    type: "POST",
    url: "/Save",
    data: jsonText,
    dataType: "json",
    //async: false            [1]
}).done(function (msg) {    
    $("#upload").click();   
});

//$("#upload").click();       [2]

Demo: http://jsfiddle.net/c2v00uxn/

Note:

  • if I uncomment [1] or [2], it does work (the file dialog appears as expected).
  • replace click() with trigger('click') doesn't work
  • replace click() with live()/on() doesn't help
  • file upload control is visible as per example (so it's not because of hidden control)
  • timeout settings for ajax do not help.

UPDATE

It's not about how to make a "click" in general, it's about how to click after an asynchronous ajax call (as of now, works only with non-asynchronous call).

user2316116
  • 6,726
  • 1
  • 21
  • 35
  • 1
    @Tushar this is not the question ! sto pasting links – Royi Namir Apr 19 '15 at 10:47
  • @Tushar I already mentioned that control is visible and that trigger() does not change anything. My question is different with one you mentioned. – user2316116 Apr 19 '15 at 10:48
  • weird indeed !.. I jsut tested it. it only works with async:false – Royi Namir Apr 19 '15 at 10:48
  • 5
    Yes this is a security(?) feature of browsers. An input type file click can only be triggered from another click, and it needs to be triggered in the same scope/at the same time as the original click. I just recently struggled with this exact problem and could not find a workaround. – powerbuoy Apr 19 '15 at 11:17
  • possible duplicate of [In JavaScript can I make a "click" event fire programmatically for a file input element?](http://stackoverflow.com/questions/210643/in-javascript-can-i-make-a-click-event-fire-programmatically-for-a-file-input) – Bryan Downing Apr 24 '15 at 04:00
  • 1
    Also: http://stackoverflow.com/questions/1829774/jquery-simulating-a-click-on-a-input-type-file-doesnt-work-in-firefox – Bryan Downing Apr 24 '15 at 04:00
  • @Bryan Downing, it's not about how to "click" in general. In my example I already do such click **and it works** but before doing it, I need an ajax call and the "click" works only when async=false. I want to know **how to click after an asynchronous ajax call**. Your links do not answer my question and please do not vote to close it. – user2316116 Apr 24 '15 at 06:14
  • 2
    To add to @powerbuoy, the reason it works synchronously is because the calls to `$("#upload").click();` are still in the call stack created by the user generated event (the click of the button that triggers the ajax). The ajax really has nothing to do with your problem — the done handler is firing successfully. It's essentially the same as running `$("#upload").click();` in the console. – Bryan Downing Apr 24 '15 at 08:16
  • 1) you marked my question as duplicate with a wrong answer. 2) I need a proof / documentation that describes that security (?) feature – user2316116 Apr 24 '15 at 13:56
  • @BryanDowning The Ajax is not *failing* (i.e., the `done` callback does run), but the fact that OP is using Ajax is very closely related to the problem (in particular in how it is different from the proposed duplicate). It seems that this question essentially reduces to "How can a trusted, user-generated click propagate its 'trusted-ness' to an asynchronous task that the click event spawns?" which is quite different from the proposed duplicate. – apsillers Apr 28 '15 at 14:30
  • @BryanDowning Note further that an async `setTimeout` and `setInterval` do allow a deferred programmatic click (within a browser-defined time window), while an Ajax load callback does not allow trusted clicks, so it appears that not all asynchronous operations are equal in this regard. (http://jsfiddle.net/c2v00uxn/56/) This seems like further evidence that use of Ajax is closely related to the trusted-click problem. – apsillers Apr 28 '15 at 14:31
  • @smirnov If requirement were possible, also possible to delay opening files dialog until hours after `click`, not only immediately following an asynchronous call? files "popups" could be opened - without user click - multiple times during browsing session? A mistaken "click" during an open `files` dialog initiated with `js` could result in multiple user files being mistakenly uploaded to intended, or unintended server? Could a) validate b) notify validation c) do , not do file upload stuff at `change` event ? See also http://stackoverflow.com/q/24688268/ , http://stackoverflow.com/q/23766086/ – guest271314 Apr 29 '15 at 21:51
  • 1) I don't need delay, it wont work. 2) "mistaken click" will not result to "mistaken upload", it only opens a standard upload box (on desktop) or a photo/gallery menu (on mobile) – user2316116 Apr 30 '15 at 05:57

3 Answers3

35

Its not possible right now to open a file popup window from an async ajax callback due to w3c recommended security features in the browsers.

In the file input element's activation behavior it first checks if the algorithm is allowed to show a popup, if not then abort the next steps without doing anything else. from w3c.org

An algorithm is allowed to show a popup if any of the following conditions is true:

  1. The task in which the algorithm is running is currently processing an activation behavior whose click event was trusted.(trusted events : Events that are generated by the user agent, either as a result of user interaction, or as a direct result of changes to the DOM, are trusted by the user agent with privileges that are not afforded to events generated by script through the DocumentEvent.createEvent("Event") method, modified using the Event.initEvent() method, or dispatched via the EventTarget.dispatchEvent() method. The isTrusted attribute of trusted events has a value of true, while untrusted events have a isTrusted attribute value of false. otherwise. http://www.w3.org/TR/2012/WD-DOM-Level-3-Events-20120614/#trusted-events.)
  2. The task in which the algorithm is running is currently running the event listener for a trusted event whose type is in the following list:

    • change
    • click
    • dblclick
    • mouseup
    • reset
    • submit
  3. The task in which the algorithm is running was queued by an algorithm that was allowed to show a popup, and the chain of such algorithms started within a user-agent defined timeframe.

w3c.org

In your code the click event is not triggered by the user but its triggered by the ajax complete callback. Here the browser declares that the event cannot be trusted to open a popup. In some browsers you can see an isTrusted attribute set to true if the event is declared as trusted.https://developer.mozilla.org/en-US/docs/Web/API/Event/isTrusted

Note

Different browsers trap the difference between a script activated cick and a real user using different methods.

What you can do in this scenario is to disable the file input button(or the entire form) and enable after ajax is done. That way the user wont click on the upload button until the ajax request gets completed. As of now there is no other way to do both in one click since there is a timeframe limit also for opening popup. When I checked in chrome the timeframe is 1000ms. 1000ms after the user action, window will not get opened.

tkay
  • 1,716
  • 14
  • 18
  • Does answer intend to indicate, or provide proof through reference to documentation, that requirement _"it's about how to click after an asynchronous ajax call"_ is not possible ? – guest271314 Apr 26 '15 at 18:21
  • 1
    @guest271314 This helps because due to all the above conditions you cannot open a file select popup in the callback of an asynchronous ajax.. Its how browsers behave. Browsers don't want us to open a file popup from an untrusted event . So my answer is abandon hope of finding a way to do what the user what trying to do.. Even if you find something you cannot rely on that since that might be working because of security flaws. – tkay Apr 28 '15 at 08:08
  • is there any way to disable this security feature if someone want to on web page ?? – Language Lassi Apr 28 '15 at 12:29
  • I don't think so. If there is a way then that is a security flaw right? Browsers will eventually come up with patches to rectify them. – tkay Apr 28 '15 at 13:50
  • @tkay, regarding your edit "That way the user wont click on the upload button until..." My behaviour is following (I already described it to one of the answers which was deleted): it is a mobile website where user enters some data and attaches images. The upload control is hidden and I trigger a click on it programmatically. If user clicked on a "photo" button and form is not yet saved I call that ajax to validate and save data and if save went well (so I need to wait for `done()`) then I could initiate an upload box opening. If save went not ok then I show an error message and no upload box. – user2316116 Apr 28 '15 at 15:17
  • 1
    Okay. What about validating form inputs on their respective change events/on change event of form input just above the photo button instead of validating on the photo button click event? That way you can ensure if the user can be allowed to attach images or not before the user clicking the photo button. You can disable the photo button until the request is completed. – tkay Apr 28 '15 at 15:30
  • 1) There is one field that is currently checked right before saving the data into a database. That field is entered manually and it should be unique within the table. The application has multiple users. If user entered the value which is exist, I cancel save and show an error. No upload box should appear 2) While I do some client side validation (mandatory fields, numeric fields, etc), it's still possible that there is an error (e.g. db server is down). In this case it's still better to ensure that save went well. – user2316116 Apr 28 '15 at 20:41
  • The only possible method is to separate file input click from the untrusted async ajax done callback and do it before user clicking the photo button. I'm afraid there is no other way to solve this issue. I don't see any problem with using form input change events in your case. And you can check the server status using polling maybe. [A basic demo](http://jsfiddle.net/c2v00uxn/69/) – tkay Apr 30 '15 at 07:25
  • 1
    1) This is a ticketing app where user needs to enter ticket # manually (e.g. "1001"). That # must be unique within the system. A ticket will be saved only when user clicks on Save or on Upload. It can be also canceled (exit without save). I can check for unique number on `onchange()` but another user could potentially enter the same # right after that, since I do not save # in the database until Save or Upload is clicked. So, your idea could work for me only if I will "block" # for a while and do not let other users enter it. – user2316116 Apr 30 '15 at 12:19
  • 2) If save in db will fail, I prefer to show an error message and not an upload box. – user2316116 Apr 30 '15 at 12:19
  • 1
    @smirnov Thanks for explaining the difficulty clearly. I think as of now there is no way to do this in a single click event. Its clear from the docs that minimum two user clicks are required in this particular scenario. – tkay May 01 '15 at 14:07
  • Also, it seems it FAILS in Incognito windows on Chrome - even clicking a file input fails. At least, some of the time. That's been my experience. Can anyone verify? – Gregory Magarshak Apr 27 '23 at 15:00
0

JQuery itself says that, trigger will not work for elements like file upload and anchor. (source - https://learn.jquery.com/events/triggering-event-handlers/)

The .trigger() function cannot be used to mimic native browser events, such as clicking on a file input box or an anchor tag. This is because, there is no event handler attached using jQuery's event system that corresponds to these events.

So in that case, you may need to create events manually using the following javascript functions.

  • document.createEvent
  • event.initMouseEvent
  • element.dispatchEvent

A sample code + definition for the above-said functions can be found in Mozilla's developer site

https://developer.mozilla.org/samples/domref/dispatchEvent.html

Maybe, this will help you.

Adersh M
  • 596
  • 3
  • 19
0

I have a solution that works for now. It is a hack, so it may not work in the near future, as it circumvents the security features mentioned by Tkay above.

I introduce a timeout that waits for the ajax request to return the data I want to check before initiating the file browser dialog.

customFileUploadButton.addEventListener('click', function(e) {

    var returnValueToCheck; //Value we want to check against

    //reference to vanilla JS ajax function that takes callback
    ajax(function(ajaxData) { 
        returnValueToCheck = ajaxData;
    });

    setTimeout(function() {
        if (returnValueToCheck !== undefined) { //dummy check for example
            file.click();
        } else {
           console.log("Criteria not fulfilled");
        }
    }, 1000);//timer should be larger than AJAX timeout
});

To be honest, I am a bit unsure why exactly this work aside from apparently passing the browser specific tests normally prohibiting this sort of behaviour. Therefor I regard it as a hack.

My example is vanilla JS, but it should be easy to create a jQuery version. See this JSFiddle for full example.

(This is my first venture into contributing, so please comment on potential errors and oversights)

Heenrik
  • 1
  • 3