it-swarm.com.de

Die programmgesteuert erzeugte/aktivierte Dateieingabe löst nicht immer ein Eingabeereignis aus

Ich habe eine Schaltfläche in meiner Webanwendung, die im Click-Ereignishandler den folgenden Code enthält:

const fileInputEl = document.createElement('input');
fileInputEl.type = 'file';
fileInputEl.accept = 'image/*';

fileInputEl.addEventListener('input', (e) => {
  if (!e.target.files.length) {
    return;
  }

  // Handle files here...
});  

fileInputEl.dispatchEvent(new MouseEvent('click'));

Manchmal (etwa 1 von 8) wird nach Auswahl der Datei das input-Ereignis nicht ausgelöst, nachdem eine Datei ausgewählt wurde. Ich vermute, dies ist ein Browser-Fehler im Lebenszyklus des Elements.

Irgendein Weg, um das Element an die Seite anzuhängen und es später zu entfernen? Wie kann man heutzutage in modernen Browsern richtig damit umgehen?

Ich teste mit Google Chrome unter Windows.

JSFiddle: http://jsfiddle.net/pja1d5om/2/

14
Brad

Zitieren Sie aus Ihrer Frage: Manchmal (ungefähr 1 von 8) Nach Auswahl der Datei wird das Eingabeereignis nicht ausgeführt wird nicht ausgelöst, nachdem eine Datei ausgewählt wurde.

Ich kann dieses Verhalten mit input und mit change Ereignissen bestätigen, indem ich Opera (Version 55.0.2994.61, neueste Version zu diesem Zeitpunkt) benutze, die Google Chrome Browser Engine "Blink" . Es passiert ungefähr 1 von 25.

Lösung

Dies liegt daran, dass Ihr Eingabeelementobjekt manchmal nach dem Schließen des Dateidialogs gelöscht wurde, weil es nicht mehr verwendet wird. Und wenn es passiert, haben Sie nicht das Ziel, das das Ereignis input oder change empfangen könnte.

Um dies zu lösen, fügen Sie einfach Ihr Eingabeelement irgendwo in das DOM ein, nachdem Sie es wie folgt als verstecktes Objekt erstellt haben:

fileInputEl.style.display = 'none';
document.body.appendChild(fileInputEl);

Und wenn das Ereignis ausgelöst wurde, können Sie es wie folgt löschen:

document.body.removeChild(fileInputEl);

Vollständiges Beispiel

function selectFile()
{
    var fileInputEl = document.createElement('input');
    fileInputEl.type = 'file';
    fileInputEl.accept = 'image/*';
    //on this way you can see how many files you select (is for test only):
    fileInputEl.multiple = 'multiple';

    fileInputEl.style.display = 'none';
    document.body.appendChild(fileInputEl);

    fileInputEl.addEventListener('input', function(e)
    {
        // Handle files here...
        console.log('You have selected ' + fileInputEl.files.length + ' file(s).');
        document.body.removeChild(fileInputEl);
    });  

    try
    {
        fileInputEl.dispatchEvent(new MouseEvent('click'));
    }
    catch(e)
    {
        console.log('Mouse Event error:\n' + e.message);
        // TODO:
        //Creating and firing synthetic events in IE/MS Edge:
        //https://docs.Microsoft.com/en-us/previous-versions/windows/internet-Explorer/ie-developer/compatibility/dn905219(v=vs.85)
    }
}
<input type="button" onclick="selectFile()" value="Select file">

Zitieren Sie aus Ihrer Kopfgeldbeschreibung: Kopfgeld wird an jemanden vergeben, der ... eine angemessene Umgehung zeigt.

Mein alter Lösungsvorschlag (jetzt irrelevant)

Wir können die Funktion setInterval verwenden, um zu überprüfen, ob der Eingabewert geändert wurde. Wir speichern intervalID in unserem neuen fileInputEl als Eigenschaft. Da wir immer ein neues Dateieingabeelement erstellen, ist sein Wert beim Start immer leer (bei jedem Klick auf die Schaltfläche). Und wenn dieser Wert geändert wurde, können wir ihn erkennen, wenn wir ihn mit einem leeren String vergleichen. Und wenn es passiert, übergeben wir unsere Funktion fileInputEl an fileInputChanged() und löschen/stoppen unsere Intervallfunktion.

function selectFile()
{
    var fileInputEl = document.createElement('input');
    fileInputEl.type = 'file';
    fileInputEl.accept = 'image/*';
    //on this way you can see how many files you select (is for test only):
    fileInputEl.multiple = 'multiple';

    fileInputEl.intervalID = setInterval(function()
    {
        // because we always create a new file input element then
        // its value is always empty, but if not then it was changed:
        if(fileInputEl.value != '')
            fileInputChanged(fileInputEl);
    }, 100);

    try
    {
        fileInputEl.dispatchEvent(new MouseEvent('click'));
    }
    catch(e)
    {
        console.log('Mouse Event error:\n' + e.message);
        // TODO:
        //Creating and firing synthetic events in IE/MS Edge:
        //https://docs.Microsoft.com/en-us/previous-versions/windows/internet-Explorer/ie-developer/compatibility/dn905219(v=vs.85)
    }
}

function fileInputChanged(obj)
{
    // Handle files here...
    console.log('You have selected ' + obj.files.length + ' file(s).');
    clearInterval(obj.intervalID);
}
<input type="button" onclick="selectFile()" value="Select file">
3
Bharata

Es scheint, dass dies ein Browser-Bug/Zufall ist und wahrscheinlich etwas mit Garbage Collection zu tun hat. Ich kann es umgehen, indem ich die Dateieingabe zum Dokument hinzufüge:

fileInputEl.style.display = 'none';
document.querySelector('body').appendChild(fileInputEl);

Wenn Sie fertig sind, können Sie es bereinigen mit:

fileInputEl.remove();
0
Brad

Das ist ein sehr interessanter Fehler, den Sie dort bekommen haben, ich konnte ihn nicht reproduzieren.

Gibt es einen Grund, warum Sie sich einer Dateieingabe so nähern, wie Sie es tun? Wollen Sie es ausprobieren und gestalten?

Ich las diesen Artikel und wendete an, was er tat. Ich fand, dass dies gut funktionierte. In diesem Artikel wird versucht, ein Etikett mit der Eingabe mit einem Attribut für das Label zu verbinden. In CSS und JavaScript wird dann das Dateieingabe-Tag ausgeblendet, und das Label fungiert im Wesentlichen als "Schaltfläche".

Zum Beispiel...

Note Ich habe einige Änderungen am Code vorgenommen, aber alle haben Osvaldas Valutis zu verdanken. Er ist der Verfasser des oben genannten Artikels bei CoDrops.

var inputs = document.querySelectorAll('.inputfile');

inputs.forEach(input => {

  var label = input.nextElementSibling,
    labelVal = label.innerHTML;

  input.addEventListener('change', function(e) {

    var fileName = '';

    if (this.files && this.files.length > 1)
      fileName = (this.getAttribute('data-multiple-caption') || '').replace('{count}', this.files.length);
    else
      fileName = e.target.value.split('\\').pop();

    if (fileName)
      label.querySelector('span').innerHTML = fileName;
    else
      label.innerHTML = labelVal;

  });

});
* {
  font-family: sans-serif;
  font-weight: 300;
}

.inputfile {
  display: none;
}

.inputfile+label {
  font-size: 1.25em;
  font-weight: 700;
  color: white;
  background-color: darkred;
  display: inline-block;
  padding: 10px;
  border-radius: 10px;
  border: 1px darkred solid;
  cursor: pointer;
}

.inputfile+label:hover {
  background-color: darkred;
}
<input type="file" name="file" id="file" class="inputfile" data-multiple-caption="{count} files selected" multiple />
<label for="file">Choose a file <span></span></label>

Jetzt weiß ich, dass dies möglicherweise nicht genau das ist, wonach Sie suchen, aber dies könnte eine alternative Lösung sein, die Sie ausprobieren könnten.

0
Andrew Gremlich