Trigger click on input=file on asynchronous ajax d

2019-01-01 13:49发布

问题:

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).

回答1:

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.



回答2:

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.



回答3:

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)



标签: jquery