Thursday, November 9, 2017

Character Viewer: How To Save User Input - part 1 - Fun With Forms

    In our last tutorial we looked at how to write JavaScript data to an HTML table. This lets us populate a table dynamically instead of having to hand-code it. Except it doesn't really, after all we had to hand-code the objects we used to populate the table. So we need a truly dynamic way to fill our table, by letting the user create the data. So for this tutorial we're going to add to our app the ability to read and save user input. Let's take another quick look at what we've got...

    So this is still what we want it to look like at the end, a table with information about our Pathfinder characters. We're just going to add a few things.

Collecting User Input- Form Controls
    HTML has several elements that are designed to gather input, they're called form controls. A <form> tag defines a group of controls that are all going to be collected together. So we'll to start our section with that. We don't have to, but it's convenient. I'm going to add a border to the form and a margin to make it stand out, plus an <h3> header and some padding since the header looked a little funny. Here's what it looks like and my code...




    Okay, so now that I've set aside the space for my form I need to put in the controls to actually collect all the data. There are a lot of form controls and I'm going to try to use a variety of them for this project so you can get to know them all (kinda like Pokemon :). The most basic control is the textbox, a box that the user can type text into. It's built from a core element <input> and given the attribute type="text" and should work great for our character's name...



     The default textbox is about 15 characters long, we could change that with CSS, but I'm leaving it for now since it works for me. If you've ever looked at the code for a form (or if you do later) you'll notice that most form controls have a name="something" attribute. The name attribute is used when sending the form's data (called "submitting" the form). I'm not going to be sending this form anywhere, I'm going to collect the data with JavaScript, so I'm using the typical id="" attribute to track everything instead.
    So we have a field that the user can type in general text, numbers or letters. Let's look at another form control. For the character's race let's say we want them to choose from a limited list. This is a tutorial and not a working program so we'll try some different things out, even if they might not be ideal for the finished (or "production") app. Let's put in a drop-down list, this will let the user select an option from a list that we provide. This has the advantage that you can prevent the user from entering invalid data (since they're limited to the list) or from mis-typing/spelling the data. The downside is that you're limiting the user to the options you provide, so you need something that is complete and unchanging, like a list of the USA's state abbrevations. So this is not the ideal control for a character's race, since there are way too many Pathfinder races to list here, but like I said we're just doing it to play with the control.
    What we need is a <select> tag that's going to wrap a bunch of <option> tags. Each option is going to have a value="" attribute that's going to be the data we save (for JavaScript to read), and the text inside the option is going to be what the user sees. Since these are going to be the same thing, we want the user to see exactly what we're going to save, it's going to look a little funny but it makes it easier for Javascript to read later.
    I'm going to add options for only a few races, and here's what my finished control looks like...



    Okay, we've got the first two things down, lets go ahead and use another new control. A character can have more than one class, so let's use a set of checkboxes. The <input type="checkbox"> creates an element with a name and a box the user can click on. This lets the user choose multiple options. So I'm going to make a checkbox for some of the basic classes, but I want to show that they all go together, so I'm going to wrap them in a <fieldset>. This element is just going to draw a border around all the checkboxes, to show they're related...



Note that in my HTML code I have each checkbox on a separate line, but I didn't put in any <br> tags so they are displaying in the page as being on a single line. That's for my sake, it's easier for me to read my own code if I put each element on a separate line - and I mention it as a reminder that how your code looks and how it displays are two different things. Use whatever look is easiest for you. Speaking of looks, I'm not a fan of how crowded this table is getting. Each element seems to be smashed against the others. I want to add some spacing. I'm going to do that by adding a some CSS to the <form> element, a line-height: 200% will space things out a bit. I'm also making the fieldset a little shorter...



That's a bit better. Good enough for now at least.
    Let's keep adding stuff to our form. Next is the character's level. So far I used the textbox, which allows the user to type in anything, numbers or letters. But a character's level is only a number, so let's use a new control that will prevent the user from accidently putting any letters in there. This is a new -input type="number"- that was added in HTML 5, so older browsers might not be able to show it, but my up-to-date Chrome will do fine. It doesn't look like much at first...


But put the mouse over it and you'll see the little up and down arrows to change the number...



    So let's put a starting value in there. I'm going to add the value="1" attribute, and levels don't go below 1, so let's also add a min="1" attribute to keep it in the right range. Also, the box is pretty long, so in CSS I'm going to set it's width to just a few characters. Here's the final look and code...




    Wow, so we finally finished the first cell of our table :)  We've done pretty good at going through all the different form elements.  So I'm going to use those same number fields for all of the attributes.  Now, I'm going to have the user input the value of the attribute, not the modifier.  The modifier is a formula, so I can have JavaScript calculate it.  This will help me avoid any mistakes or funny math from the user.  This is a general practice when you're making forms, called "validation."  You want to make sure that you're getting the right data from your form.  So you start by limiting the potential mistakes as much as you can, and then you validate your data to make sure it's what you expect.  Users make mistakes, not because people are bad and lie (though some few do) but because people are people and they make mistakes.  So you want to structure your forms in a way that minimizes those mistakes and keeps an eye out for them.
   Another thing is that my form is looking a little thin.  So far it's just one row of inputs on the left of the screen.  That means there's a lot of wasted whitespace.  I'm not going to worry about this being seen on a mobile device or other small screen - that could be a tutorial of it's own.  So I'm also going to make 3 columns, by using <div>s and setting them to float in CSS.  So with the attribute inputs and column layout here's what the form will look like and it's code...




To make columns you can set a <div> to position: relative and float: left but those need to be inside a container with position: absolute which I set the <form> to.  Take that out the absolute container and you get a mess...



    The absolute element is like a fixed reference for the relative ones to move around. And float: left was described to me like a balloon: that element will float to the top of it's container and then drift to the left. It makes the div's align at the top and sit side-by-side. So I've got 3 columns of content in the form. I had to remove the width attribute I had added to the fieldset, now that I've got the columns I don't need to artificially shorten the fieldset.
    We're almost there. For the next two cells of our table, the weapons and armor, let's go ahead and use one other type of form control: the radio button. A radio button is like a checkbox, but it is round instead of square and the user can only select one option out of the group. It's an <input type="radio"> but each one in the set needs the same name="" attribute so the browser knows they all go together. Again this would not be a great choice for a production app, but we're learning and playing. So here are a few weapons as radio button options...



    I set the "none" radio button to be checked by default, so that something would be selected. Pretty easy. I also centered the attributes since I thought they looked better that way. Let's finish the form and add the last few controls. I'm putting in a number field for the base attack bonus, I'll have JavaScript add the Str or Dex mod depending on the weapon. Then another set of radio buttons for armor. Lastly another number field for hit points. And that will give us everything that goes into our table...



    Not bad, just a final finishing touch and we'll be done with our form. First, we need some way to add the character, so I'm going to add another <input> element with the type="button" that will be used to launch a JavaScript function to read the form and add it to the table. Another thing to consider though is if the user wants to clear the whole form, say they change their mind about what they put in? Well, in that case let's add a button to reset the form, of course it's <input type="reset">...



    And that makes our form. Go team us! ;) You can download the code from my Google Drive here and play with it yourself. Now that we've created a form all we need to do is make it work. That's going to take some JavaScript to read our form, format it, and then add it to the table - all of which we'll get into next week. Until then!


No comments:

Post a Comment