Ivanka Todorova
Ivanka Todorova

Reputation: 10219

File input triggers change event twice (or more)

I have a basic input[file] element which I hide. When you click on the #holder a file explorer pops up. But selecting a file triggers the console.log() line to be executed twice (on my computer).

Beware: the following code below crashes my Chrome tab.

You should better run it as a separate file. Cannot provide a "working" demo, but this is the closest I can get to MCVE.

var element = document.getElementById('holder');

element.onclick = function(e) {
  var input = document.getElementById('file-input');
  input.click();
  input.addEventListener("change", function(evt) {
    console.log(evt);
    Phimij.addFiles(input.files);
  }, false);
};
#holder {
  border: 10px dashed #ccc;
  width: 300px;
  height: 300px;
  margin: 20px auto;
}
#holder.hover {
  border: 10px dashed #333;
}
#file-input {
  display: none;
}
<div id="holder">
  <input type="file" multiple id="file-input" />
</div>

Upvotes: 3

Views: 4542

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074276

click events bubble up the ancestry tree. That means a click on your input will bubble up to your #holder element and fire your click handler on it. In your click handler on #holder, you fire the click event on the input. That's why your browser crashes: You've triggered an infinite loop.

The solution is to hook click on the input and tell it not to bubble (propagate); see flagged lines (but keep reading, further notes below):

var element = document.getElementById('holder');
// **** Added vvvv
document.getElementById('file-input').addEventListener("click", function(evt) {
  evt.stopPropagation();
}, false);
// *** Added ^^^^
element.onclick = function(e) {
  var input = document.getElementById('file-input');
  input.click();
  input.addEventListener("change", function(evt) {
    console.log(evt);
    // Phimij.addFiles(input.files);
  }, false);
};
#holder {
  border: 10px dashed #ccc;
  width: 300px;
  height: 300px;
  margin: 20px auto;
}
#holder.hover {
  border: 10px dashed #333;
}
#file-input {
  display: none;
}
<div id="holder">
  <input type="file" multiple id="file-input" />
</div>


There are a few other things I'd change. You're adding a change handler to the input every time there's a click on #holder; you really only want to do that once. I'd also add that handler before triggering the click.

So for what it's worth, some changes I'd make:

var element = document.getElementById('holder');
var input = document.getElementById('file-input');

element.addEventListener("click", function() {
  input.click();
}, false);

input.addEventListener("click", function(evt) {
  evt.stopPropagation();
}, false);
input.addEventListener("change", function(evt) {
  console.log(evt);
  // Phimij.addFiles(input.files);
}, false);
#holder {
  border: 10px dashed #ccc;
  width: 300px;
  height: 300px;
  margin: 20px auto;
}
#holder.hover {
  border: 10px dashed #333;
}
#file-input {
  display: none;
}
<div id="holder">
  <input type="file" multiple id="file-input" />
</div>

Upvotes: 4

Related Questions