0

Disclaimer: This is my first HTML, CSS, JavaScript project. Please excuse any misused vocabulary here.

I am making a basic webform for the user to do some pass fail checks. I set all the radio buttons to be required. The implementation of the basic project I modified skips the button portion and instead uses an onclick function to convert any non blank value into a JSON list. Then I wrote some JavaScript to allow the user to download that list.

While debugging I realized that the submit button ignored the required attributes but the new clear all button I'm using correctly acknowledges them.

I am currently debugging 4 approaches, but decided consulting someone with more experience would teach me better fundamentals.

There is a local storage JavaScript as well if anyone needs to see that, I can edit it in.

$(document).ready(function() {
  $("#btn").click(function(e) {
    var jsonData = {};
    var formData = $("#myform").serializeArray();
    $.each(formData, function() {
      if (jsonData[this.name]) {
        if (!jsonData[this.name].push) {
          jsonData[this.name] = [jsonData[this.name]];
        }
        jsonData[this.name].push(this.value || '');
      } else {
        jsonData[this.name] = this.value || '';
      }
    });
    console.log(jsonData);
    e.preventDefault();

    function download(content, fileName, contentType) {
      var a = document.createElement("a");
      var file = new Blob([content], {
        type: contentType
      });
      a.href = URL.createObjectURL(file);
      a.download = fileName;
      a.click();
    }
    download(JSON.stringify(jsonData), 'list.json', 'json');
  });
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myform" type="post">
  <fieldset>
    <legend>Some label </legend>
    <div class="elements">
      <label for="name">1) Test 1:</label>
      <input required="required" type="radio" value="pass" name="usb1" size="20" /> [PASS]
      <input required="required" type="radio" value="fail" name="usb1" size="20" /> [FAIL]
    </div>
    <p></p>
    <div class="elements">
      <label for="name">2) Test 2:</label>
      <input required="required" type="radio" value="pass" name="usb2" size="20" /> [PASS]
      <input required="required" type="radio" value="fail" name="usb2" size="20" /> [FAIL]
    </div>
    <!-- ignores required fields -->
    <div class="submit">
      <input type="submit" id="btn" name="btn" class="btn" value="Submit" />
    </div>
    <!-- Works same as above as far as I'm aware -->
    <button type="button" id="btn" name="btn" class="btn">Submit</button>
    <!-- //working correctly -->
    <button type="button" id="clr" name="clr" class="clr">Clear</button>
  </fieldset>
</form>
Alessio Cantarella
  • 5,077
  • 3
  • 27
  • 34
MickyG
  • 3
  • 3
  • well button click has nothing to do with submission – epascarello Jan 14 '20 at 20:23
  • I've never understood the desire to use required radio buttons. Radio buttons are supposed to be for a choice between several options, one of which is a default option. So there should be three radio buttons: unknown, pass, fail, with unknown being the default. The validation would be that either pass or fail must be checked, not unknown. Then people could undo their choice if they accidentally check pass or fail before they were ready. – Heretic Monkey Jan 14 '20 at 21:16
  • @HereticMonkey I think what you propose is one way of handling it if you are running your own form validation (which is probably best practice anyway, for data integrity purposes). However, there are validation triggers built into forms on the DOM, and the `required` attribute on a radio button group is one of them. I'd rather not have extra inputs in my code to have to maintain and debug if possible, personally. But I think we are well into the realm of opinion here. – wlh Jan 14 '20 at 21:20

2 Answers2

0

A <button> of type=button does not attempt to validate using the required parameter, It needs to be of type=submit, also you don't need to required=required you can just put required.

If all you want is for the required parameter to work you can do it like this,

<!DOCTYPE html>
<html>
  <head>
    <title>SO question</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
  </head>

  <body>
    <canvas id="slot"> </canvas>
    <form id="myform" type="post">
      <fieldset>
        <legend>Some label</legend>
        <div class="elements">
          <label for="name">1) Test 1:</label>
          <input required type="radio" value="pass" name="usb1" size="20" /> [PASS]
          <input required type="radio" value="fail" name="usb1" size="20" /> [FAIL]
        </div>
        <p></p>
        <div class="elements">
          <label for="name">2) Test 2:</label>
          <input required type="radio" value="pass" name="usb2" size="20" /> [PASS]
          <input required type="radio" value="fail" name="usb2" size="20" /> [FAIL]
        </div>
        <!-- Works same as above as far as I'm aware -->
        <button type="submit" id="btn" name="btn" class="btn">Submit</button>
      </fieldset>
      <script>
        $(document).ready(function() {
          $('#btn').click(function(e) {
            var jsonData = {};
            var formData = $('#myform').serializeArray();
            $.each(formData, function() {
              if (jsonData[this.name]) {
                if (!jsonData[this.name].push) {
                  jsonData[this.name] = [jsonData[this.name]];
                }
                jsonData[this.name].push(this.value || '');
              } else {
                jsonData[this.name] = this.value || '';
              }
            });
            console.log(jsonData);
          });
        });
      </script>
    </form>
  </body>
</html>

You can still use the $("#btn").click() to build your objects or any other logic after this method. The upside is the built in HTML <form> validation, the downside is that you still need to deal with empty inputs, but it gives you options.

JDunken
  • 465
  • 2
  • 7
  • adjusting type=submit does not evaluate the attributes as expected either. – MickyG Jan 14 '20 at 20:57
  • If the `input` with the `required` parameter is inside of a `form` and the `button` of `type=submit` is in the same form, it does work. – JDunken Jan 14 '20 at 21:08
  • This won't work with the JS above once only one `type=submit` `input`/`button` is left in the code. It has to do with the event being listened to `onclick` instead of `onsubmit` – wlh Jan 14 '20 at 21:16
  • It has nothing to do with how many buttons are in the form. See my edited code with only one button. – JDunken Jan 14 '20 at 21:32
0

The main issue here is the event on which you are listening. The click event will not bubble up to cause a form submission since you are calling preventDefault. Your code will still run even if the form is not complete.

However, if instead you listen to the submit event on the form with the id myForm, this will still trigger validation. Check out this article on the submit event: https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement/submit_event

Change this:

$("#btn").click(function(e){

To this:

$("#myform").submit(function(e){

As a side note, you should also include a label for each input (at the minimum for accessibility purposes). I included an example of this with a comment in the snippet below. Here is a helpful article on using the for attribute on labels: https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor

For more reference, see these other SO questions and answers:

How to use the "required" attribute with a "radio" input field

preventDefault() stops form validation

        $(document).ready(function() {
        /* Change the selector from listening on the click event of the button to the submit event of the form */
            $("#myform").submit(function(e){
                e.preventDefault();
                var jsonData = {};
                var formData = $("#myform").serializeArray();
                // console.log(formData);
                $.each(formData, function() {
                    if (jsonData[this.name]) {
                        if (!jsonData[this.name].push) {
                            jsonData[this.name] = [jsonData[this.name]];
                        }
                        jsonData[this.name].push(this.value || '');
                    } else {
                        jsonData[this.name] = this.value || '';
                    }
                });
                console.log(jsonData);
                


                function download(content, fileName, contentType) {
                    var a = document.createElement("a");
                    var file = new Blob([content], {type: contentType});
                    a.href = URL.createObjectURL(file);
                    a.download = fileName;
                    a.click();
                }


                download(JSON.stringify(jsonData), 'list.json', 'json');
            });
        });
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<form id="myform" type="post">
  <fieldset>
    <legend>Some label </legend>
    <div class="elements">
    <!-- unrelated, but the for attribute should equal the id of the element for which it is a label, and for accessiblity purposes, its best that each input has it's own label -->
      <span>1) Test 1:</span>
      <label for="usb1-pass"><input id="usb1-pass" required type="radio"  value="pass" name="usb1"  size="20"  /> [PASS]</label>
      <label for="usb1-fail"><input id="usb1-fail" required type="radio"  value="fail" name="usb1"  size="20"  /> [FAIL]</label>
    </div>
    <p></p>
    <div class="elements">
      <span>2) Test 2:</span>
      <label for="usb2-pass"><input id="usb2-pass" required type="radio"  value="pass" name="usb2"  size="20"  /> [PASS]</label>
      <label for="usb2-fail"><input id="usb2-fail" required type="radio"  value="fail" name="usb2"  size="20"  /> [FAIL]</label>
    </div>
    <!-- with the change in how you listen, this will work -->
    <div class="submit">
       <input type="submit" id="btn" name="btn" class="btn" value="Submit" />
    </div>
    <!-- This won't work as a submit button 
    <button type="button" id="btn" name="btn" class="btn">Submit</button>
    -->
    <!-- //working correctly -->
    <button type="button" id="clr" name="clr" class="clr">Clear</button>
    <!--<span class="elements">Press "Ctrl + Shift + J" to see sent JSON in console: <span>-->
  </fieldset>
</form>
wlh
  • 3,426
  • 1
  • 16
  • 32