In HTML, file is one of the type attributes of the input element in a form. See under form for a good overview of forms; I just want to add some detail specific to the file type.

Visually, <input type=file> creates a text box with a browse button next to it, like the combination <input type=text><input type=button value="Browse">. The text part of it can be varied with the size and maxlength attributes. As with an ordinary text box, <input type=file size=20 maxlength=100> allows 20 characters to be visible, and 100 characters altogether to be entered, scrolling along the text box.

There doesn't seem to be any way of changing the literal text "Browse" (well, it says that on my browser though it's probably browser-dependent), nor the placement relationship between the text and button parts. This is a single component, and while the literal "Browse" would be the value of a button, it's not a distinct attrribute of a file input, the value of which is the text in the box, intended to be a file-name.

When you click the button, a system-dependent file-selection dialogue opens. This is out of the control of HTML. Once you open/okay/whatever a file, its name gets placed in the value of the text box. As fully specified file-names on a typical machine can get quite long, this overrides the length restriction imposed by maxlength. That only restricts what you can enter by typing.

There's not a lot your HTML page can do with a file: neither HTML nor JavaScript can read them; so usually what it does is have the form submit them to some other script, program, or volcano god, to wreak its will on. Now form submissions usually include name/value pairs: you give an input element a name attribute and it passes on whatever value the element had when you pressed Submit. In the case of a file, this is probably unwanted behaviour, since the value is just the file name. So you have to specify that what gets passed is the file contents, thus: <form enctype="multipart/form-data" method=post>

There is a sub-attribute accept which is said to restrict the kind of files the element will accept. Its value is a comma-separated list of MIME encodings; for example <input type=file accept="images/*">. I find my browser button actually accepts any kind even if this is set, but perhaps it won't post it if you have method=post -- I didn't check.

One good thing HTML and JavaScript together can do with files is manipulate images. This is really why I'm writing this node: one little gotcha is worth documenting. Here's the bones of how to change an image:

<form name=myform>
<img name=myimg src=mydefault.jpg>
<input type=file name=myfileinput onChange=ChangeImg()>
<script>
  function ChangeImg () {
    document.myimg.src =
      document.myform.myfileinput.value;
  }
</script>
</form>
If you just type the name into the text box, this will happily accept spaces and relative file-names, such as images/cute kittens.jpg. But it didn't work when I chose a file from the browser. Why not?

Relative file names are relative to some base it already knows about, such as http://www.example.com/ or some base directory. The images/cute kittens.jpg gets appended to that to form the full name, which therefore begins with a protocol such as http://. Now all the browser does is return a file name as its value. This probably begins with the top-level disk identifier, such as C:/ or Macintosh HD/. It doesn't begin with a protocol -- it looks like a relative file name, http://www.example.com/C:/... So you need to... or at least on my browser and system I needed to, since this could well be dependent... prefix the absolute file name with the protocol file:/// (with three slashes) to get the src property to recognize it:

<script>
  function ChangeImg () {
    document.myimg.src =
      'file:///' + document.myform.myfileinput.value;
  }
</script>

Another small gotcha is that if you read off the src property (e.g. by assigning its value to a text box or showing it in an alert), you'll find it's been escape()'d, the spaces having been turned into %20 and in my case the slashes having been reversed. This is automatic -- just because it comes out like that doesn't mean you have to feed it in with escape(). Though you can, it seems harmless -- but don't escape() the file:/// prefix! (Another thing I did wrong.)

...Oh damn. Except that now doesn't work for hand-entered relative names. * sigh * Well I'm sure there's a solution if I keep looking... Test for a colon in the name string and only add the protocol if there is one?

HTML pedants might like to use lots of redundant punctuation in their working code and not leave it off like me. Please /msg me with real (non-pedantry) errors etc.