Update
The issue described in this post is most likely resolved by patch release elm/file 1.0.2!
Related:
- Cross-browser compatibility fix for File.Select.file(s) in elm/file
- https://github.com/elm/file/issues/4
Thank you everyone!
This is seemingly a known, but not-definitely-resolved? problem.
Reference: javascript - Event onChange won’t trigger after files are selected from code-generated INPUT element - Stack Overflow
Background
File.Select.file
and File.Select.files
do the following (source):
- Generate a
input[type="file"]
element behind the scene (not rendered) - Set
change
event handler on the element, handling a new file selection - Dispatch an artificial
click
event to the element, triggering file selector window
This works, and is actually a method well-known even MDN documentation has it (so-called “hidden file input” trick).
As the linked MDN page says, this trick is employed for replacing “ugly” browser-default file input element with your own styled buttons/elements.
(And as the elm/file documentation states, for security reasons, the above listed sequences must be executed directly upon some user inputs (like actual clicks). Though this restriction is already considered and properly handled by kernel codes, so at least in my experience, it’s working reliably)
Here’s working example (Ellie):
module Main exposing (main)
import Browser
import File exposing (File)
import File.Select
import Html exposing (..)
import Html.Attributes exposing (..)
import Html.Events exposing (onClick)
type alias Model =
{ file : Maybe File }
init : () -> ( Model, Cmd Msg )
init () =
( { file = Nothing }, Cmd.none )
type Msg
= ReqFile
| GotFile File
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
ReqFile ->
( model, File.Select.file [ "*/*" ] GotFile )
GotFile f ->
( { model | file = Just f }, Cmd.none )
view : Model -> Html Msg
view model =
div []
[ button [ onClick ReqFile ] [ text "File Select" ]
, div []
[ text <|
case model.file of
Just f ->
File.name f ++ " (" ++ File.mime f ++ ")"
Nothing ->
"(empty)"
]
]
main : Program () Model Msg
main =
Browser.element
{ init = init
, view = view
, update = update
, subscriptions = always Sub.none
}
Symptom
Again, it works, but not reliably. In a small example like the one above, it should almost always work. But when this pattern of codes are introduced in real projects, it “sometimes” fails.
The actual symptom is that after you selected a file (or files) and hit OK in the file selector window, change
event is “sometimes” not fired (OR, fired but not caught), thus not registering the file selection!
I encountered this on Chrome (both Windows 10 and mac), Safari. But NOT in Firefox (mac).
By quoting “sometimes” I emphasize this behavior happens very unpredictably. My gut feeling tells in larger and busier apps that run many event handling other than file selections, this happens more, even to the level of “almost never works”. To support this, my app has Time.every
ticks for triggering background polling tasks, in that app I see the problem very often. But while disabling Time.every
ticks, the problem almost ceases to happen (but still happens occasionally.)
Since I myself have not thoroughly investigated conditions for reproduction, I am a bit hesitant to open an issue, but after some searching I found that this was actually a known issue around this “hidden file input” trick. That is the link I put at the top of the issue.
javascript - Event onChange won’t trigger after files are selected from code-generated INPUT element - Stack Overflow
Also there is another one: javascript - input[type=file] change event are lost occasionally on Chrome - Stack Overflow
The situation described in these SO posts are basically equivalent to my case with File.Select.file
although both are 2-year-old threads.
Speculations by posters includes:
- race condition around call stack
- node is gone (perhaps GCed?) before file selection
- issue around registering event handler (node is gone BEFORE handler is registered?), this I doubt though
I actually managed to reproduce the behavior just once on Ellie with the example app above, but not consistently after that. At successful(?) reproduction, I searched around directories in file selector window for several minutes with occasional waits in between (taking time can contribute?).
ToDos/Requests
- Since I do not have concrete condition to reproduce the problem, I want to hear from folks who happen to have seen similar symptoms.
- Also, if you have reliable workaround for this issue (preferably using
File.Select.file
OR other means to use styled file input trigger element.) - Hints for narrowing down the problem, since I do have a project and test data that can reliably reproduce the symptom, I can try your suggestions.
- Actually, the code is available here (linking to related commit) but not yet advertised openly
Thank you for your attention!