User Tools

Site Tools


en:web_development:forms:javascript

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Next revision
Previous revision
en:web_development:forms:javascript [2022/01/25 07:06]
mag created
en:web_development:forms:javascript [2023/08/16 09:33] (current)
Line 608: Line 608:
 <code>                <input type="button" name="submit" value="Add Appearance" onclick="verifyAddAppearance()"> <code>                <input type="button" name="submit" value="Add Appearance" onclick="verifyAddAppearance()">
                 <input type="button" name="submit" value="Delete Appearances" onclick="verifyDeleteAppearance()"></code>                 <input type="button" name="submit" value="Delete Appearances" onclick="verifyDeleteAppearance()"></code>
-  + 
 +==== Required Fields ==== 
 +  Our 'heroes' table in the database requires that all fields contain content. 
 +  * Required fields are generally denoted by a red asterisk, which we can add with a combination of HTML and CSS. 
 +<code>                        <label>Alias:<span class="required">*</span></label></code> 
 +<code>                        <label>Identity:<span class="required">*</span></label></code> 
 +<code>                        <label>Side:<span class="required">*</span></label></code> 
 +<code>                        <label>First Appearance:<span class="required">*</span></label></code> 
 +<code>                        <label>Source of Power:<span class="required">*</span></label></code> 
 +<code>.required { 
 +  color: red; 
 +  vertical-align: super; 
 +}</code> 
 +  * <html>vertical-align: super</html> is the CSS equivalent of the <html><sup></html> tag to make the asterisk superscript. 
 +  * We can use Javascript to check that all required fields are completed before submitting the form. 
 +  * In 'form.js', add a function called 'verifyAddHero'
 +<code>function verifyAddHero() { 
 + 
 +}</code> 
 + 
 +==== Text Input ==== 
 + 
 +  * Let's start with the check for the alias. 
 +<code>    message = ""; 
 +    if (document.getElementsByName('alias')[0].value == "") { 
 +        message += "The alias is required. "; 
 +    } 
 +    console.log(message);</code> 
 +  * <html>document.getElementsByName('alias')</html> is a list of all elements named 'alias'
 +  * We want the first, so we use <html>[0]</html>
 +  * Then if the content is empty, we want to create a message reminding the user that 'alias' is required. 
 +  * Except, we don't want to create it here because we want a message for each of the missing inputs. 
 +  * Instead, we create the <html>message</html> variable first, then add a message to it if necessary. 
 +  * <html>console.log(message);</html> will display the message in the console so you can see the result now. We'll add the message to the 'status' block later. 
 +  * Save, upload and submit the form without an alias to check that it works. 
 +  * Let's do the same for 'identity'
 +<code>    if (document.getElementsByName('alias')[0].innerHTML == "") { 
 +        message += "The alias is required. "; 
 +    } 
 +    if (document.getElementsByName('identity')[0].innerHTML == "") { 
 +        message += "The identity is required. "; 
 +    } 
 +    console.log(message);</code> 
 +  * We don't need to add a check for 'side' or 'power' because we automatically select an option and there is no way to select nothing. 
 + 
 +==== Console Commands ==== 
 +  * We do need to check for a 4-digit number in the 'year' input. 
 +  * This is quite tricky, so open your console and type the following. 
 +<code>document.getElementsByName('year')[0]</code> 
 +  * You'll see some grey text underneath showing you details of the 'year' input. 
 +  * Press 'Enter' and you'll get the whole object with all its information and functions. 
 +  * Press the up arrow to restore the text you just entered. 
 +  * You can now add to this text in any way you like. 
 +  * Add '.value' at the end to see the value of the input field. 
 +  * Type a number into the field and press 'Enter' in the console to see the number returned there. 
 +  * Before we go further, let's give the console a command to assign the value to a variable. 
 +  * Press the up arrow to get the value again, and add 'year = ' at the front and ';' at the back as follows. 
 +<code>year = document.getElementsByName('year')[0].value;</code> 
 +  * You'll see the value you've entered. 
 +  * Type 'year' and see that value again. Your console now knows that 'year' is the value in the 'year' field. 
 + 
 +==== Comparisons ==== 
 +  * Now change the number to text and see that it shows <html> "" </html> for the same command ('year'), so we have a problem. 
 +  * We need first to check whether there is any recognisable input at all. 
 +  * Try the following in the console. 
 +<code>document.getElementsByName('year')[0].value == ""</code> 
 +  * You should see 'True'
 +  * <html> == </html> is a comparison test, which returns 'True' if the expressions on either side are the same and 'False' otherwise. So this statement means 'the year is empty.' 
 + 
 +==== Check Number Length ==== 
 +  * Change the year to a number. 
 +  * Type the following into the console and check that it shows the number of digits in your number. 
 +<code>year.length</code> 
 +  * You should recognise this from earlier lessons. Here, <html>.length</html> doesn't count the number of elements in an array. It counts the number of characters in text or digits in a string. 
 +  * We want a message to appear if the number is anything other than 4 digits. Try 
 +<code>year.length == 4</code> 
 +  * You should see the opposite of what we want. It will show 'True' if 'year' is a 4-digit number and 'False' otherwise. We need to use '!' to turn it around. Try 
 +<code>!year.length == 4</code> 
 +  * You should see that this doesn't work. Rather than taking the opposite of the whole statement <html>year.length == 4</html>, this takes the opposite of <html>year.length</html> and compares it to 4. 
 +  * To make '!' apply to the whole expression we need to enclose it in <html> () </html> 
 +<code>!(year.length == 4)</code> 
 +  * Try a few numbers to ensure that works to your satisfaction. 
 + 
 +==== Combining Expressions ==== 
 +  * Now, since we want to write a message in both cases 'if the entered value is empty (not a number) OR if the number is NOT 4 digits' we need to combine them. 
 +  * We can replace the 'OR' in the above expression with <html> || </html> in Javascript. The final expression will look like this. 
 +<code>if ((year == "") || !(year.length == 4))</code> 
 +  * Although we don't need <html> () </html> around the first expression, keeping the two parts consistent helps reduce confusion. 
 +  * For comparison, a logical 'AND' is written as <html> && </html>. We could say the above as 'if the entered value is NOT BOTH a number AND 4 digits long', which we would write as <html>if (!(year == "") && (year.length == 4))</html>
 +  * Try this and use the expression you're most comfortable with. 
 +  * We can now add this code into our verifyAddHero function. 
 +<code>    var year = document.getElementsByName('year')[0].value; 
 +    if ((year == "") || !(year.length == 4)) { 
 +        message += "The first appearance is required and must be a 4-digit year. "; 
 +    }</code> 
 +  * Test this to make sure it works as you expect. 
 + 
 +==== Display Message ==== 
 +  * Finally, we need to display the message. 
 +<code>    var status = document.getElementById('status'); 
 +    if (message == "") { 
 +        console.log('ready to submit'); 
 +    } else { 
 +        status.firstElementChild.innerHTML = message; 
 +        status.style.display = 'block'; 
 +    }</code> 
 +  * I've identified the 'status' element first because I'll use it a couple of times. 
 +  * If the message is blank (ie all fields have an entry) then we are ready to submit the form (or do further checks) but for the moment, we just display a message in the console. 
 +  * If there is a message, then we need to display that for the user. This time we're not removing the message after 10 seconds. 
 +  * Save, upload and submit the form with various fields complete. Check that the response is as expected. 
 + 
 +==== Deferred Submission ==== 
 +  * Try this code in your console. 
 +<code>document.getElementsByName'add_hero')[0].submit()</code> 
 +  * You should see a message that says this 'is not a function'
 +  * This is because we've named our form buttons 'submit'
 +  * By naming them 'submit' we've overwritten the function 'submit()'
 +  * We can easily fix this by renaming our buttons 'formSubmit' in 'form.php'
 +<code>                <input type="button" name="formSubmit" value="Add / Update Hero" onclick="verifyAddHero()"></code> 
 +<code>                <input type="button" name="formSubmit" value="Add Appearance" onclick="verifyAddAppearance()"> 
 +                <input type="button" name="formSubmit" value="Delete Appearances" onclick="verifyDeleteAppearance()"></code> 
 +  * Our processing code will no longer recognise the data, though, so we need to reflect this change in the processing code in the 4th line from the top. 
 +<code>    switch ($_POST['formSubmit']) {</code> 
 +  * Now replace <html>console.log('ready to submit');</html> in 'form.js' with the submit command you tested in the console. 
 +<code>    var status = document.getElementById('status'); 
 +    if (message == "") { 
 +        document.getElementsByName('add_hero')[0].submit(); 
 +    } else { 
 +        status.firstElementChild.innerHTML = message; 
 +        status.style.display = 'block'; 
 +    }</code> 
 +  * Save, upload and test that it works as you expect. 
 + 
 +==== Selected Value ==== 
 +  * Let's set up the second form to submit new appearances. 
 +<code>function verifyAddAppearance() { 
 +    message = ""; 
 +}</code> 
 +  * To show how we can check the value of a Select Box, let's first add an empty option to the box in 'form.php'
 +<code>                        <label>Hero</label> 
 +                        <select name="alias"> 
 +                            <option value=""></option> 
 +<?php 
 +    // display list of heroes 
 +    while($row = $hrslt->fetch()) { 
 +?> 
 +                            <option value="<?php echo $row['alias']; ?>"><?php echo $row['alias']; ?></option> 
 +<?php 
 +    } 
 +?> 
 +                        </select></code> 
 +  * See the new empty option in the 3rd line? 
 +  * In the console, create a variable called 'alias' for the Select Box value. 
 +<code>alias = document.getElementsByName('alias')[1].value;</code> 
 +  * Remember that this is the second field we've named 'alias' in the page, so we need to use the index of <html>[1]</html>
 + 
 +==== Not Equal ==== 
 + 
 +  * We can check if the value is empty by typing <html>alias == ""</html>
 +  * So we can add code to show a message as follows. 
 +<code>function verifyAddAppearance() { 
 +    var message = ""; 
 +    var alias = document.getElementsByName('alias')[1].value; 
 +    if (alias == "") { 
 +        message += "The Hero is required. "; 
 +    } 
 +}</code> 
 + 
 +==== Checkbox Status ==== 
 +  * Checking a checkbox is more complex in standard Javascript. You need to check each of the values to see which are checked. 
 +  * First we need to set a variable 'movie' for all the checkboxes. 
 +  * If you try <html>var movie = document.getElementsByName('movie[]')[0];</html> in the console you'll see that it is 'undefined', so we need to find another way. 
 +  * Try this in your console. 
 +<code>document.getElementById('movieList').getElementsByTagName('input')[1]</code> 
 +  * You should see the second movie highlighted and <html><input id="" type="checkbox" name="movie[]" value="Iron Man"></html> element shown in the console. 
 +  * We can check whether this movie is selected by doing this. 
 +<code>document.getElementById('movieList').getElementsByTagName('input')[1].checked</code> 
 +  * Try that yourself. 
 + 
 +==== While Loop ==== 
 + 
 +  * Now, to loop through all the list items, we can do this. 
 +<code>    for (n = 0; n < movieList.length; n++) { 
 +         
 +    }</code> 
 +  * There's not always need to check every checkbox, though, and for a long list this could take time. As soon as we know one of them is checked, we can stop. 
 +  * For this, we can use a 'while statement'
 +  * A 'while statement' says to do something while a condition is true. 
 +  * In this case, we need to check that the checkbox actually exists (<html>n < movieList.length</html> will do this) AND that the movie isn't checked, which we can test with <html>!movieList[n].checked</html>). 
 +  * Our loop is then as follows. 
 +<code>    while((n < movieList.length) && (!movieList[n].checked)) { 
 +        n++; 
 +    }</code> 
 +  * Copy this into your 'verifyAddAppearance' function. 
 +  * Now, if we actually get all the way through the list and have found nothing checked, we want to add a message, but if we do find something, we want to stop it. 
 +  * Our 'message' variable isn't very useful for this, so we need another. 
 +  * Create a variable called 'NoAppearance' to track the number of unchecked movies. 
 +<code>    var message  = ""; 
 +    var noAppearance = 0;</code> 
 +  * We can then increment the number every time we find an unchecked box. 
 +<code>    while((n < movieList.length) && (!movieList[n].checked)) { 
 +        noAppearance++; 
 +        n++; 
 +    }</code> 
 +  * At the end, we can compare the number of unchecked boxes with the total number of movies. If it's less, then at least one is checked. Otherwise, if noAppearance is equal to the number of movies, we need to show a message. 
 +<code>    if (noAppearance == movieList.length) { 
 +        message += "You need to add at least one movie. "; 
 +    }</code> 
 +  * There's one last small problem we need to address with this form. 
 +  * If the 'new' checkbox is checked, we need to ensure that there is a movie title in the adjacent text box. 
 +  * How do we check the status of 'new' checkbox? It's the second last 'input' (the last is the one for the movie name) so the index is 2 less than the length of 'movieList'
 +<code>movieList[movieList.length-2].checked</code> 
 +  * The value of the movie name input is <html>movieList[movieList.length-1].value</html> and we only need a message if both the checkbox is checked AND the name field is blank. So our code needs to be as follows. 
 +<code>    if ((movieList[movieList.length-2].checked) && (movieList[movieList.length-1].value == "")) { 
 +        message += "If you check the last box, you need to name the movie."; 
 +    }</code> 
 +  * Add this to your 'addAppearance' function. 
 +  * Finally, we can determine whether to submit the form or show a message in the same way as for the previous form. 
 +<code>    var status = document.getElementById('status'); 
 +    if (message == "") { 
 +        console.log('ready to submit'); 
 +    } else { 
 +        status.firstElementChild.innerHTML = message; 
 +        status.style.display = 'block'; 
 +    }</code> 
 +  * Save, upload and test this code. 
 + 
 +==== Sharing Code ==== 
 +  * Before we enable the submission, take a look at the whole 'verifyAddAppearance' function and notice that aside from the actual submit command, it all applies to 'verifyDeleteAppearance' too. 
 +  * If we rewrite the entire code, then find we need to change something (for example, we update the form), we need to change this code twice too. Far better to share the same code for both functions. 
 +  * To do this, we create a new function. Let's call it 'verifyAppearance' and move all the shared code into the new function. 
 +<code>function verifyAppearance() { 
 +    var message  = ""; 
 +    var noAppearance = 0; 
 +    var alias = document.getElementsByName('alias')[1].value; 
 +    if (alias == "") { 
 +        message += "The Hero is required. "; 
 +    } 
 +    var movieList = document.getElementById('movieList').getElementsByTagName('input'); 
 +    var n = 0; 
 +    while((n < movieList.length) && (!movieList[n].checked)) { 
 +        console.log(n); 
 +        noAppearance++; 
 +        n++; 
 +    } 
 +    console.log(noAppearance); 
 +    if (noAppearance == movieList.length) { 
 +        message += "You need to add at least one movie. "; 
 +    } 
 +    if ((movieList[movieList.length-2].checked) && (movieList[movieList.length-1].value == "")) { 
 +        message += "If you check the last box, you need to name the movie."; 
 +    } 
 +    console.log(message); 
 +}</code> 
 +  * We then call 'verifyAppearance' from our 'verifyAddAppearance' function. 
 +<code>function verifyAddAppearance() { 
 +    // check form data 
 +    verifyAppearance(); 
 + 
 +    // take action - display message or submit 
 +    var status = document.getElementById('status'); 
 +    if (message == "") { 
 +        console.log('ready to submit'); 
 +    } else { 
 +        status.firstElementChild.innerHTML = message; 
 +        status.style.display = 'block'; 
 +    } 
 +}</code> 
 +  * The problem now is that the main function doesn't know the value of 'message' so it can't decide whether to submit or show the message. 
 +  * We need to 'return' message to the main function. Add this code to the end of verifyAppearance. 
 +<code>    return(message);</code> 
 +  * Then we can assign that value to 'message' in verifyAddAppearance. 
 +<code>    var message = verifyAppearance();</code> 
 +  * Save, upload and test. Is it working correctly? 
 + 
 +==== Uncheck Checkbox ==== 
 +  * We still have to write the code for 'verifyDeleteAppearance'
 +  * It's almost exactly the same as for 'verifyAddAppearance' except for the submit command which we haven't written yet, so let's start by copying the code. 
 +<code>function verifyDeleteAppearance() { 
 +    // check form data 
 +    var message = verifyAppearance(); 
 + 
 +    // take action - display message or submit 
 +    var status = document.getElementById('status'); 
 +    if (message == "") { 
 +        console.log('ready to submit'); 
 +    } else { 
 +        status.firstElementChild.innerHTML = message; 
 +        status.style.display = 'block'; 
 +    } 
 +}</code> 
 +  * There is one other difference. We don't want to delete 'new' because it doesn't exist. 
 +  * Rather than checking whether it's checked asking the user to uncheck it, let's provide a better user experience by unchecking it automatically. 
 +  * Try this in your console first. 
 +<code>movieList[movieList.length-2].checked = false;</code> 
 +  * That should set the checkbox to an unchecked state. 
 +  * We want to do the same thing in our function, but it doesn't know about 'movieList' yet. 
 +  * We can't easily return that from 'verifyAppearance' because we are already returning a value, but we can send it as a parameter when we call the function (or we could just define it twice). 
 +<code>    // check form data 
 +    var movieList = document.getElementById('movieList').getElementsByTagName('input'); 
 +    var message = verifyAppearance(movieList);</code> 
 +  * Now 'verifyAppearance' can obtain this variable as a parameter in the top line. 
 +<code>function verifyAppearance(movieList) {</code> 
 +  * And we can delete the line where we assign it in that function. 
 +  * Now, we can uncheck the checkbox in 'verifyDeleteAppearance'
 +<code>    // uncheck 'new' checkbox 
 +    movieList[movieList.length-2].checked = false;</code> 
 +  * Save, upload and test that everything works as it should. 
 +  * If it does, the final step is to add the commands to submit each form. 
 +<code>        document.getElementsByName('add_appearance')[0].submit();</code> 
 +<code>        document.getElementsByName('add_appearance')[0].submit();</code> 
 +  * Save, upload and test again. 
 +  * Congratulations. You have now created a functional form. 
 + 
 +[[en:web_development:forms:exercises|Next: Exercises]]
en/web_development/forms/javascript.1643123188.txt.gz · Last modified: 2023/08/16 09:33 (external edit)