Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why the selection is wrong when file input is hidden?

Tags:

jquery

I was 100% sure this code works, but in one special case it fails - spent an hour to find the bug (could not reproduce it on JSFiddle). Finally I found the cause, but I do not know why this happens. It happens only when I hide the file inputs and the result is: whichever file I change the first label is selected and changed, please take a look:

var activateFileSelection = function( container ) {
    var container = container || $('body');
    container.find(':file').each(function(i) {
        var thisInput = $(this);
        var thisLabel = thisInput.siblings('label');
        if (thisLabel.length > 0 && !thisLabel.hasClass('file-input-label')) {
            var thisLabelDefaultText = thisLabel.html();
            thisLabel.addClass('file-input-label');
            thisInput.on('change', function(e) {
                if (thisInput.val()) {
                    thisLabel.html(thisInput.val());
                } else {
                    thisLabel.html(thisLabelDefaultText);
                };
            });
        };
    });
};
activateFileSelection();
input {
    display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <label for="myfile">select 1</label>
    <input id="myfile" type="file" />
</div>
<div>
    <label for="myfile">select 2</label>
    <input id="myfile" type="file" />
</div>
<div>
    <label for="myfile">select 3</label>
    <input id="myfile" type="file" />
</div>

Also on JSFiddle.

If the input is not hidden, everything works fine: fiddle.

As nevermind noted below, duplicate input IDs may have something with the issue, still in script I do not address and IDs. Also, the change of each file input triggers the change of label text, but wrong one.

like image 876
skobaljic Avatar asked Mar 11 '23 12:03

skobaljic


2 Answers

The layout of the HTML code may suggest that each label is linked to the input field next to it, but it is not the case. Since the for attribute of the three labels is "myfile", all three labels are in fact linked to the first input field, the one that is found with document.getElementById("myfile").

Therefore, when you click on any label, the first input field is used. The adjacent label, obtained with thisInput.siblings('label') in your code, is then updated after the file selection. That is true even when the 3 input fields are visible.

On the other hand, if you click directly on an input field, that input field is used and the corresponding label is updated.

The following snippet sends a message to the console to indicate which file input is used.

var activateFileSelection = function (container) {
    var container = container || $('body');
    container.find(':file').each(function (i) {
        var thisInput = $(this);
        var thisLabel = thisInput.siblings('label');
        if (thisLabel.length > 0 && !thisLabel.hasClass('file-input-label')) {
            var thisLabelDefaultText = thisLabel.html();
            thisLabel.addClass('file-input-label');
            thisInput.on('change', function (e) {
                console.log("Input used: " + thisInput.data("field"));
                if (thisInput.val()) {
                    thisLabel.html(thisInput.val());
                } else {
                    thisLabel.html(thisLabelDefaultText);
                };
            });
        };
    });
};
activateFileSelection();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<p>Test by clicking on label 3 vs clicking on input field 3:</p>
<div>
    <label for="myfile">select 1</label>
    <input id="myfile" type="file" data-field="input1" />
</div>
<div>
    <label for="myfile">select 2</label>
    <input id="myfile" type="file" data-field="input2" />
</div>
<div>
    <label for="myfile">select 3</label>
    <input id="myfile" type="file" data-field="input3" />
</div>
like image 155
ConnorsFan Avatar answered Mar 13 '23 01:03

ConnorsFan


You need to use the proper ID for your label.

var activateFileSelection = function( container ) {
    var container = container || $('body');
    container.find(':file').each(function(i) {
        var thisInput = $(this);
        var thisLabel = thisInput.siblings('label');
        if (thisLabel.length > 0 && !thisLabel.hasClass('file-input-label')) {
            var thisLabelDefaultText = thisLabel.html();
            thisLabel.addClass('file-input-label');
            thisInput.on('change', function(e) {
                if (thisInput.val()) {
                    thisLabel.html(thisInput.val());
                } else {
                    thisLabel.html(thisLabelDefaultText);
                };
            });
        };
    });
};
activateFileSelection();
input {
    display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
    <label for="myfile1">select 1</label>
    <input id="myfile1" type="file" />
</div>
<div>
    <label for="myfile2">select 2</label>
    <input id="myfile2" type="file" />
</div>
<div>
    <label for="myfile3">select 3</label>
    <input id="myfile3" type="file" />
</div>

EDIT: I understand your question now. I'm not sure what the cause is. It's likely just a bug with how the browser interprets it is my guess. The proper way to do it is to add unique IDs.

like image 25
robere2 Avatar answered Mar 13 '23 02:03

robere2