RSS

GMail-Like Attachment Forms using DOM Scripting

Gmail

The web development discussion for today will be about dynamically adding HTML elements using DOM and Javascript. We will be emulating a feature you’ve probably seen all over the web, most notably on GMail when it let’s you add attachments to emails. GMail presents the user with an attachment button. The user can then click it to reveal one attachment. All of this takes place dynamically without any browser refreshes. The user can then add more and more attachments all dynamically. This effect is very useful and also intuitive, which is great for us designers.

You will want to download this zip file to get all the images and sample code. Now let’s learn some DOM and try to emulate this cool effect. As a sidenote, I totally blundered the spelling of “attachment” in the Javascript code. It’s all fixed in this tutorial, but just as a note, it sucks when you misspell something in your code. Ok, now let’s start.

Let’s take a look at where we are headed:

You can play around with this. Click the plus and minus buttons to see it dynamically add and delete. You can choose files, but I haven’t implemented an uploading system or the like since this is not our goal. I’d also like to point out that this effect can be implemented on so many possible objects. Do not simply limit it to attachments. It’s very useful for advanced searching and many other areas. What we want to do first is set up some CSS, so take a look:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
a img {
	border: 0;
	outline: 0; /*no firefox dotted border*/
}
body {
	font-family: "Lucida Grande", Arial, Sans-Serif;
}
input,label,img {
	display: inline;
	vertical-align: middle;
}
 
.attachment {
	margin-bottom: 5px;
}

Some quick notes. I always use this a img selector in any site or code I make. I just recently learned of this outline property. It will remove the dotted border around images when they are clicked in Firefox. I’ve always hated that about Firefox, but now I can fix it! Anyways, the other things to note are the input,label,img properties, which are a nice way to get all these elements to line up along a vertical center. The other selectors are simple preferences for my example. There isn’t much to this CSS, so let’s move on to some HTML.

1
2
3
4
5
6
7
<div id="attachment_container">
	<input type="hidden" value="1" id="attachment_counter" />
    <div id="attachment_1" class="attachment">
    <label>Select file #1: </label><input type="file" size="14" id="file_1" name="file_1" /><a href="" onclick="removeAttachmentElement(1);return false;"><img id="RemoveButton_1" src="minusButton.png" onmousedown="this.src='minusButtonDown.png';" onmouseup="this.src='minusButton.png';" alt="Remove" /></a>
    </div>
</div>
<a href="" onclick="addAttachmentElement();return false;"><img id="AddButton_1" src="plusButton.png" onmousedown="this.src='plusButtonDown.png';" onmouseup="this.src='plusButton.png';" alt="Add" /></a>

Let me note here, that if you wanted to not even show an attachment field at start, you could remove that div with id=”attachment_1″. Also, be sure to reduce your counter value to 0. Now, let me walk you through the code. We start with a container div that will hold all of our attachment rows. You will probably want to surround this div in a form element so you can do something with the results the user gives. Inside this div we create a hidden input with a value of 1 (representing how many attachments we have) and give it an id.

Each time we create an attachment row we will create a new div inside this container. We have one here now. Its id is attachment_1 we also assign it to the attachment class. Each of these inner divs will contain a label telling the user what the field is for (in this case Select file #x). It contains the input type that we want. It then has an image of a minus sign (with some mousedown and mouseup effects) wrapped inside of a link tag which will call the removeAttachmentElement() function. We pass in the parameter 1 since this specific attachment is an id of 1.

Finally, outside all of these divs, we have the add button. It’s also a simple image wrapped in a link that calls addAttachmentElement(). Now that we are finished with all of our HTML, we will need to move onto our Javascript that powers all of this.

1
2
3
4
function removeAttachmentElement(num_id) {
	var container = document.getElementById('attachment_container');
	container.removeChild( document.getElementById('attachment_'+num_id) );
}

Let’s start with this short code. This will be the code used to remove an attachment div. Line 2 sets our container div to the variable container. We then call the function removeChild() on this container. To it, we pass the div that contains the attachment. In the HTML above, it would be the div with id attachment_1. What we are using here is DOM (Document Object Model) scripting. It allows us to simply remove a child element from the HTML using javascript, immediately rendered in the browser. Now that we have the simple remove code out of the way, let’s get to the adding code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
function addAttachmentElement() {
var container = document.getElementById('attachment_container');
var counter = document.getElementById('attachment_counter').value;
counter++;
document.getElementById('attachment_counter').value = counter;
var attachment_div = document.createElement('div');
attachment_div.setAttribute('id','attachment_'+counter);
attachment_div.setAttribute('class','attachment');
 
	var attachment_label = document.createElement('label');
	attachment_label.innerHTML = "Select file #"+counter+":&nbsp;";
	attachment_div.appendChild(attachment_label);
 
	var attachment_input = document.createElement('input');
	attachment_input.setAttribute('type','file');
	attachment_input.setAttribute('size','14');
	attachment_input.setAttribute('id','file_'+counter);
      attachment_input.setAttribute('name','file_'+counter);
	attachment_div.appendChild(attachment_input);
 
	var attachment_a2 = document.createElement('a');
	attachment_a2.setAttribute('href','');
	attachment_a2.setAttribute('onclick','removeAttachmentElement('+counter+');return false;');
		var attachment_img2 = document.createElement('img');
		attachment_img2.setAttribute('id','RemoveButton_'+counter);
		attachment_img2.setAttribute('src','minusButton.png');
		attachment_img2.setAttribute('alt','Remove');
		attachment_img2.setAttribute('onmousedown',"this.src='minusButtonDown.png';");
		attachment_img2.setAttribute('onmouseup',"this.src='minusButton.png';");
		attachment_a2.appendChild(attachment_img2);
 
	attachment_div.appendChild(attachment_a2);
 
container.appendChild(attachment_div);
}

This function might look a little complex, but it is rather simple. Let’s break it down. The first two lines set up variables that point to our container object and a counter, which will keep track of the number of attachments we have. Line 4 increases the counter and then the 5th line saves that update to our hidden input field. Now we are on to the dynamic DOM code. On line 6 you see document.createElement(’div’). This function will create a new html object of type div. See, it’s actually really easy. The next two lines set attributes for this div object. Those 3 lines, 6-8 are basically like:

1
<div id="attachment_#" class="attachment">

We are just turning HTML creation into scripting functions. The next 2 lines of code create the label tag. Line 12 then takes that label tag and adds it inside of the attachment_div tag. What lines 6-12 look like in HTML is:

1
2
3
<div id="attachment_#" class="attachment">
<label>Select the file ## </label>
</div>

This should hopefully start to make sense. The next set of code creates the input button and then adds it also to the attachment_div. The next part of code creates an a tag with the same settings as our first attachment (see the first HTML code section). We then add a child to the a tag. This child is the image of the minus button. Finally, on line 31, we add the a tag (which also includes the image tag) to the attachment_div. And then on line 33, we add this entirely created new div to our container. And it is displayed to the screen. Quite simple, and very efficient.

Well folks, that’s about it to the tutorial. You should now be prepared to create your own GMail-like attachment forms that dynamically update. You should also have a good solid start into DOM scripting. Please let me know if you have any questions, comments, suggestions, or code complaints by leaving a comment. I hope you enjoyed this and look ahead for more tutorials coming soon. You may want to add this blog to your RSS reader by clicking here.

12 Replies

I understand the solution but as far as I know, File input will not submit file if ‘name’ property is not specified and in your case it is not.So despite good looking interface such solution will not work.If you will specify name attribute you will see unexplainable behavior. Each File input control added to DOM contain value after common dialog box appears and file chosen, but when try submit the Form -submit would not happend the same time one of File inputs will loose value.Every next attempt to submit whould clear next File input When no Filled File inputs left submit will send data to server , however no files for you to save…

If i miss something dont hesitate to contact me.

Lenny

Lenny on 5/4/2007 at 06:34

Lenny: I’ve added in the name parameter to both the HTML and the JS. I think this should work fine since each input will have its own id and name field.

Dustin Bachrach on 5/4/2007 at 18:31

Not really related to the post: What plugin are you using for syntax highlighting?

kenny on 5/8/2007 at 21:22

kenny: I’m using a plugin called SyntHihol (scroll down some). It’s a really nice plugin.

Dustin Bachrach on 5/8/2007 at 22:13

i noticed that in IE if you click on the minus symbol (the remove one) the page just shoots out to some funny page. the problem was here

attachment_a2.setAttribute(’href’,”);

you should change this to

attachment_a2.setAttribute(’href’,'javascript:removeAttachmentElement(’+counter+’);’);

so that it works in IE…

otherwise this is one cool script and i’m loving it….

Knowledge Chikuse on 5/17/2007 at 01:44

Hey,
I understand how this script works, and it’s awesome, but for the life of me I can’t get a working upload system.
Could you give me an example on how I could, using this script, upload using PHP?
If not, no problem… I will play around :)

Richard on 6/15/2007 at 16:31

Richard, it’s kind of hard to help you there, but you have inspired me to write a tutorial on how to do some cool ajax uploading with php. I’m going to write a tutorial on this to be posted on my blog in a few days. Check back for it. Thanks.

Dustin Bachrach on 6/15/2007 at 16:35

It is indeed hard ;) I am close… I can upload the first file, but I am sure yours will be far superior… so I will wait,
Thanks again - and I am glad to be of help, ha

Richard on 6/15/2007 at 16:46

Hi..
Its not working with IE ,but why?
plz let me know/.

Rajesh on 9/18/2007 at 07:57

Not working with IE?

Rajesh on 9/18/2007 at 07:58

Hi
I had tried to get values in attached file using ASP(request.Form) but I getting only counter value(number file input box).How can get the file input(container) value instead of counter value.

shihab on 11/4/2007 at 01:32

need your idea on hwo to tweak this.

like the example u show, everytime add new, at first, it will keep on adding 1 to the previous id number right.. so it shows like..

#1
#2
#3
#4
#5

AND, once we remove any of the element, for example #3, it will become like this right..

#1
#2
#4
#5

and if we add new after this, it will continue the numbering from the last counter, which will show something like this..

#1
#2
#4
#5
#6
#7
#8 …and so on…

my question is, IS IT POSSIBLE to tweak the number displayed, to make it re-calculate the whole numbering sequence? to make it look back like 1, 2, 3, 4, 5..etc so doesnt matter which line we remove, it will always display the correct number sequence.

since i just want the numbering for display purpose, i have the idea of making another counter, which is specifically for display. but i dont know how to do this in javascript. do we have to use ajax to auto update the numbering? any tips or hint is appreciated..

Pai on 3/6/2008 at 10:10

Have anything to say?

« Cable Confusion- A Guide to Home Entertainment | Live Search As You Type and Live Sorting Tables With JS »