0

SOLVED

The problem, alerted by @CodeThing 's comment, was that there was another version of the form on the page. This wasn't obvious: for Django developers encountering this problem, check for any {% include %} tags in your template, as this other version of the form (containing identically named elements) was inside a modal included in the same page. Neither JS or Django will throw this as an error, and it is not obvious when just looking through the template, which is why it was hard to find: you have to use Inspect and search for it.

Key Principle: If you encounter this issue, use Inspect to check for any other elements with the same 'id', and if they exist remove them. Then the code should work perfectly.


Aim

  • I have two inputs: (1) a 'file-input', and (2) a 'text-input'
  • I want to listen for a 'change' in 'file-input' (i.e. successful choice of file), and upon such a change set the 'text-input' value to the name of the file.

Problem

  1. The 'change' event is not triggering when I use addEventListener
  2. When I use the input's 'onchange' attribute, it does trigger, but I still don't get access to files

Context: This is for a Django project, with Bootstrap 5 on the frontend (NO JQuery). I am using Django Crispy Forms where possible, though I have removed them from this file (to see whether that was what was causing the problem - alas, it wasn't).


Attempted Solutions

I know this should be easy. I have seen lots of similar posts about this, and I set up a 'dummy' HTML script to ensure I'm not going mad. This is posted below and it works perfectly.

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Document</title>
</head>
<body>

<input type="file" name="file" id="file-input" accept=".mp3,.mp4,.mpeg,.mpga,.m4a,.wav,.webm">
<input type="text" name="text" id="text-input">

<script>
const fileInput = document.getElementById("file-input");
const textInput = document.getElementById("text-input");

console.log(fileInput);
console.log(textInput);

fileInput.addEventListener('change', () => {
    console.log('change registered');
    if (fileInput.files.length > 0) {
        console.log(fileInput.files);
        console.log(fileInput.files[0].name);
        textInput.value = fileInput.files[0].name;
    }
});
</script>
    
</body>
</html>

The output is as expected for this dummy file - i.e. it works:

  • input id="file-input" type="file" name="file" accept=".mp3,.mp4,.mpeg,.mpga,.m4a,.wav,.webm"
  • input id="text-input" type="text" name="text"
  • change registered
  • FileList [ File ]
  • interview.mp4*

Problematic Code

Now my Django project's template is set up to replicate as close as possible this dummy set up (see the relevant part below):

<form id="file-form" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    <div class="my-4 px-1">
        <input type="file" name="file" id="file-input" accept=".mp3,.mp4,.mpeg,.mpga,.m4a,.wav,.webm">
    </div>
    <div class="my-3 px-1">
        <input type="text" name="text" id="text-input">
    </div>
    <button type="submit" class="btn btn-add">
        Transcribe
    </button>
    <script>
        const fileInput = document.getElementById("file-input");
        const textInput = document.getElementById("text-input");

        console.log(fileInput);
        console.log(textInput);

        fileInput.addEventListener('change', () => {
            console.log('change registered');
            if (fileInput.files.length > 0) {
                console.log(fileInput.files);
                console.log(fileInput.files[0].name);
                textInput.value = fileInput.files[0].name;
            }
        });
    </script>
</form>

But the output is incorrect - no change event fires!

i.e. I just get the below:

  • input id="file-input" class="form-control form-control-file" type="file" name="file" required=""
  • input id="text-input" type="text" name="text"
  • NOTHING ELSE

I have attempted to fix this by using an 'onchange' attribute on the input itself. This does register a change event, but the fileInput.files is still an empty list, so that doesn't work.

I have checked these SO answers, but they don't help - they work just like it does in the dummy file: SO Answer 1 SO Answer 2

Am I missing something? Why does this work in the dummy plain HTML version, but not in the Django version? The script is definitely running (it correctly console.logs both inputs); it cannot be crispy forms, I have removed it from the template; it can't be the or csrf_input, it still doesn't work when they're removed. I am so baffled by this!

Any and all help hugely appreciated!

  • 1
    Can you inspect element and see what's going on after page refresh... See if the ids are correct. The id should be unique on the page. Make sure there is no other element present with the same id. – CodeThing Jun 09 '23 at 09:46
  • Thanks you so much for asking @CodeThing --- that was exactly the problem! I had a modal at the top of the file which also contained the form (used for Mobiles), and this contained elements with the same ID. Removing the modal meant the problem disappeared. Thanks hugely! I will update my question with this answer – KingRanTheMan Jun 09 '23 at 10:14
  • Glad that your problem is solved. Cheers! – CodeThing Jun 09 '23 at 10:37

1 Answers1

0

I just tried it and it works:

change registered FileList [ File ] WIN_20230601_16_52_27_Pro.mp4

M.Ryan
  • 126
  • 5