Repository: sveltejs/svelte (JavaScript)
Date: 2019-07-28 17:16:31+00:00
Task type: Bug Fix

Issue:
bind:files doesn't work on safari. But works in chrome
<!--
Thanks for raising an issue! (For *questions*, we recommend instead using https://stackoverflow.com and adding the 'svelte' tag.)

To help us help you, if you've found a bug please consider the following:

* If you can demonstrate the bug using https://svelte.dev/repl, please do.
* If that's not possible, we recommend creating a small repo that illustrates the problem.
* Make sure you include information about the browser, and which version of Svelte you're using

Reproductions should be small, self-contained, correct examples – http://sscce.org.

Occasionally, this won't be possible, and that's fine – we still appreciate you raising the issue. But please understand that Svelte is run by unpaid volunteers in their free time, and issues that follow these instructions will get fixed faster.

If you have a stack trace to include, we recommend putting inside a `<details>` block for the sake of the thread's readability:

<details>
  <summary>Stack trace</summary>

  Stack trace goes here...
</details>
-->


Here's the repl.

https://svelte.dev/repl/61c6df2593cf4667ac36fb4c75c56012?version=3.6.7

```
<script>
	let name = 'world';
	
	let files = []
	
	$: {
		console.log(files)
	}
</script>


<input
		type="file"
		accept="image/*"
		class="w-full h-full cursor-pointer"
		bind:files />
```

Tried to open console. And then input a file.

On chrome it should log something. But on safari it's not working


Issue discussion:
can confirm. receiving events in chrome and firefox. 
You can use change event.
https://svelte.dev/repl/f4e2ed04dfda4b2f82ee870fafcd997e?version=3.6.7

```
<script>
	let name = 'world';
	
	let files = []
	
	$: {
		console.log(files)
	}
	const changed = (event)=>{
		console.log('changed', event)
		files = event.target.files;
	}
</script>


<input
		type="file"
		accept="image/*"
		class="w-full h-full cursor-pointer"
		bind:files
		on:change={changed}
/>
```
Yup, can confirm @dishuostec's is working. Thanks man

I'll keep this issue still open tho. Perhaps `bind:files` is easier.
I'm assuming the issue here is that file input elements don't fire the `input` event when they're changed? I don't have access to Safari to check. [MDN](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file) reports that `change` and `input` events are supported on file inputs. I don't see anything there about differences between what these events means, and `change` is what's used in the code example there. It might be safe in all browsers to just implement this binding with `change` instead of `input`.
I've just checked this on safari and file inputs do indeed fire a `change` event when the input (file) is changed.

Solution patch:
diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts
--- a/src/compiler/compile/render_dom/wrappers/Element/index.ts
+++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts
@@ -26,7 +26,7 @@ const events = [
 		event_names: ['input'],
 		filter: (node: Element, _name: string) =>
 			node.name === 'textarea' ||
-			node.name === 'input' && !/radio|checkbox|range/.test(node.get_static_attribute_value('type') as string)
+			node.name === 'input' && !/radio|checkbox|range|file/.test(node.get_static_attribute_value('type') as string)
 	},
 	{
 		event_names: ['input'],
@@ -38,7 +38,7 @@ const events = [
 		event_names: ['change'],
 		filter: (node: Element, _name: string) =>
 			node.name === 'select' ||
-			node.name === 'input' && /radio|checkbox/.test(node.get_static_attribute_value('type') as string)
+			node.name === 'input' && /radio|checkbox|file/.test(node.get_static_attribute_value('type') as string)
 	},
 	{
 		event_names: ['change', 'input'],


Test patch:
diff --git a/test/js/samples/input-files/expected.js b/test/js/samples/input-files/expected.js
--- a/test/js/samples/input-files/expected.js
+++ b/test/js/samples/input-files/expected.js
@@ -19,7 +19,7 @@ function create_fragment(ctx) {
 			input = element("input");
 			attr(input, "type", "file");
 			input.multiple = true;
-			dispose = listen(input, "input", ctx.input_input_handler);
+			dispose = listen(input, "change", ctx.input_change_handler);
 		},
 
 		m(target, anchor) {
@@ -43,7 +43,7 @@ function create_fragment(ctx) {
 function instance($$self, $$props, $$invalidate) {
 	let { files } = $$props;
 
-	function input_input_handler() {
+	function input_change_handler() {
 		files = this.files;
 		$$invalidate('files', files);
 	}
@@ -52,7 +52,7 @@ function instance($$self, $$props, $$invalidate) {
 		if ('files' in $$props) $$invalidate('files', files = $$props.files);
 	};
 
-	return { files, input_input_handler };
+	return { files, input_change_handler };
 }
 
 class Component extends SvelteComponent {