Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Javascript click() operation on input of type "file" called after a component is mounted does not work as expected in Chrome #7028

Closed
klehelley opened this issue Nov 9, 2017 · 2 comments

Comments

@klehelley
Copy link

Version

2.5.2

Reproduction link

https://github.com/klehelley/filepicker-anomaly

Steps to reproduce

The instructions are provided in the README.md of the linked GitHub project. This project contains 3 codebases, with 2 of them demonstrating the issue (the last one works but does not use VueJS, it is only there to demonstrate the expected behaviour):

  • vuejs-webpack (requires installation of NodeJS)

    • Initialize the project and start the local dev server
    cd vuejs-webpack/
    npm install
    npm run dev
    • Access the local server with Chrome browser
    • Click the "Add a file" button
  • vuejs-no-webpack

    • Just open index.html with Chrome browser
    • Click the "Add a file" button

What is expected?

A new list element is added to the displayed component AND a file picker opens.

What is actually happening?

A new list element is added to the displayed component BUT no sign of a file picker, as if no interaction happened with the <input> element hidden in the component.


The observed browser behaviour looks as if the call to click() in the mounted function for the FileUpload component were ignored. Following my tests, it seems however that the call is not ignored, and the issue is more complex than that.

We stumbled upon the issue after our first VueJS development (used in a Java portlet) was rolled out to Production. According to the user who reported it, it happened when using Firefox, but it worked well with Chrome for them. We have been unable to reproduce the issue the way they described, actually having the code work with Firefox but not with the versions of Chrome/Chromium we tested (see README of the linked Github project for the list of tested browsers).

I have spent the last two days trying to fix the issue, and trying to single out what the actual root cause is. Do note that I am not a front-end development expert, so maybe there is an anomaly in the code that I haven't noticed.

When debugging with Chrome, I checked that the function defined for mounted is called (it is). I even managed to see the 'click' event captured and processed by jQuery (not present in the reproduction code provided, but we use it in our portal, so I first suspected our rather old jQuery version and VueJS not working well together, but it does not seem to be the issue here), so it is not like the call to click() was ignored by the browser.

At this point I am still usure as to whether it is an actual bug in VueJS, or if the fault lies in Chrome (or my code).

@LinusBorg
Copy link
Member

LinusBorg commented Nov 9, 2017

Hi @klehelley and thanks for this detailed reproduction.

What's going on?

Chrome only opens the file picker when the click()event's programmatic invocation happened during a user-interaction (i.e. a user clicking a button).

Now on the surface, this is what happens, right? The user clicks "Add file", and a file input is added and clicked programmatically. But the issue is that Vue's update mechanism after data changes runs asynchronously. So the call stack of the original "Add file" click event has already finished before the new component is created.

Therefore, the click() call in the new component's mounted hook is not triggered by a user-event, from Chrome's perspective, and consequently is ignored.

You can see Chrome's behaviour, without Vue and very simplified, here: https://jsfiddle.net/0L794s53/1/

So how to solve this?

My suggestion would be to change the Files.vue component.

I would add a hidden file input to that component and use it for every new file. After the file has been selected, read it from the input, add it to the file list, and reset the file input.

That way, the FileUpload.vue component itself would never contain an actual file input.

If that doesn't work for you, because you actually need an input per file (I can't think of a reason but you might very well have one), you will have to come up with another way to have the input already in the DOM before that button is clicked. maybe always have a "new" file in the this.files list and hide the last entry until the user selected something, or whatever.

Closure & Further support

I think this is not something we can solve in Vue internals - the asynchronous update queue is here to stay. Rather, you will have to find ways to work around this, as described above. If you need further help with this, Please join us on forum.vuejs.org or chat.vuejs.org and we can work this out.

@klehelley
Copy link
Author

Thanks for your detailed answer.

I wasn't aware of that mechanism in Chrome, but that makes sense. I'll be working on changing the internals of that component to have a single <input type="file"/> element in Files.vue then. :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants