Bootstrap: Using `attr()` for custom file input content

Created on 1 Apr 2016  路  6Comments  路  Source: twbs/bootstrap

Currently the .file-custom class uses a pseudo-element for setting the "Choose file..." string. Instead of hardcoding that string in the styles, we could instead defer to the markup by using the attr() function.

<label class="file">
  <input type="file" id="file">
  <span class="file-custom"></span>
</label>

could become:

<label class="file">
  <input type="file" id="file">
  <span class="file-custom" data-content-value="Choose file..."></span>
</label> 

with css like this:

.file-custom:after {
  content: attr(data-content-value);
}

As noted in the existing v4 documentation, there is no way to dynamically update that string without using javascript. However, even with javascript it's not easy to update the content value of the pseudo element. By moving the value to the markup, it's much easier to make changes using javascript.

The attr function seems to be well supported. If there is a different attribute other than data-content-value, that'd be fine too.

I'd be happy to make a pull request, just let me know.

css v4

Most helpful comment

@psoots solution is working like a charm.
I just add data-content-value to the span :

<label class="file col-sm-offset-2 col-sm-10">
    <input type="file" id="form_profilePicture" name="form[profilePicture]">
    <span class="file-custom" data-content-value="Choose file..."></span>
</label>

Then I change the .file-custom::after into _custom-forms.scss like this :

.file-custom::after {
    content: attr(data-content-value);
}

And then, I just add these jQuery lines to make it work with all the input[type="file"] on the page :

$('input[type="file"]').on('change', function() {
    $(this).next('.file-custom').attr('data-content-value', $(this)[0].files[0].name);
});

Thanks @psoots for this little solution that is working like a charm :+1:

All 6 comments

I'm following the above method and it is working like charm. With pSeudo element we can't do much. Please do change design.
+1

@psoots solution is working like a charm.
I just add data-content-value to the span :

<label class="file col-sm-offset-2 col-sm-10">
    <input type="file" id="form_profilePicture" name="form[profilePicture]">
    <span class="file-custom" data-content-value="Choose file..."></span>
</label>

Then I change the .file-custom::after into _custom-forms.scss like this :

.file-custom::after {
    content: attr(data-content-value);
}

And then, I just add these jQuery lines to make it work with all the input[type="file"] on the page :

$('input[type="file"]').on('change', function() {
    $(this).next('.file-custom').attr('data-content-value', $(this)[0].files[0].name);
});

Thanks @psoots for this little solution that is working like a charm :+1:

For Bootstrap 4 Alpha 3, the markup has been changed:

For the HTML, it is now:

<label class="custom-file">
  <input type="file" name="upload" class="custom-file-input" />
  <span class="custom-file-control" data-content-value="Choose file..."></span>
</label>

For the CSS:

.custom-file-control:lang(en)::after {
  content: attr(data-content-value);
}

For JS:

$('input[type="file"]').on('change', function() {
    $(this).next('.custom-file-control').attr('data-content-value', $(this)[0].files[0].name);
});

For me I had to change slightly the JS to make the above solution work:

$(document).on('change', 'input[type="file"]', function() { $(this).next('.custom-file-control').attr('data-content-value', $(this)[0].files[0].name); });

Here's one solution in React + JSX (using the CSS above from @pinglamb ).

Tested with Bootstrap 4 Alpha 4.

<label className="custom-file">
  <input
    type="file"
    className="custom-file-input"
    ref={(input) => this.fileInput = input}
    onChange={() => {

      // files.length is 0 if no file was chosen.
      const files = this.fileInput.files

      this.setState({
        chosenFileName: files.length ? files[0].name : null
      })
    }}
  />
  <span
    className="custom-file-control"
    data-content-value={ this.state.chosenFileName || "Choose file..." }
  />
</label>

Closing as duplicate of #20813 given that issue has more activity around it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

bellwood picture bellwood  路  3Comments

ghost picture ghost  路  3Comments

knownasilya picture knownasilya  路  3Comments

devfrey picture devfrey  路  3Comments

iklementiev picture iklementiev  路  3Comments