Welcome to the Treehouse Community

Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.

Looking to learn something new?

Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.

Start your free trial

JavaScript

Bill Dowd
Bill Dowd
7,681 Points

Where am I going wrong with form submission?

Before I added the menu buttons, this worked, but now it submits to the controller.php instead of capturing in JQuery. It's the top .submit(function(evt)... and it worked with "form" originally, but I've tried "#config_form" and "#config_1". Nothing stops the submission.

The getConfigurations() function builds the forms for the config DB results.

Note: the DB update works just fine.

<script>
    $(document).ready(function() {
        $('#config_1').submit(function(evt) { // This captures the config results DB updates "Save" button
            evt.preventDefault();
            var id = $(this).find("span").attr("id"); // Finds the span id attribute
            $('#' + id).show(); // Shows any faded out "Saved!" text
            var url = $(this).attr("action");
            var formData = $(this).serialize();
            $.post(url, formData, function(response) {
                $('#' + id).text("Saved!").fadeOut(3000);
            }); // end post
        }); // end submit

        $('.setup_main_menu').submit(function(evt) { // This captures the menu buttons clicks
            evt.preventDefault();
            var url = $(this).attr("action");
            var formData = $(this).serialize();
            $.ajax({
                type : 'POST',
                url : url,
                data : formData,
                success: function(data) {
                    $('#output').html(data);
                }
            }); // end ajax
        }); // end submit

    }); // end ready
</script>

Controller.php:

// This file processes all form submissions

if($_REQUEST['form_id'] == "config") {
    extract($_REQUEST);
    if(setConfigurations($setting, $value)) {
        // return to the page
    }
    else {
        print "There was a problem writing to the database.";
    }
//  return getConfigurations();
}
else if($_REQUEST['form_id'] == "inmates"){

}
else if($_REQUEST['form_id'] == "units"){

}
else if($_REQUEST['form_id'] == "lessons"){

}
else if($_REQUEST['form_id'] == "certificate"){

}
else if($_REQUEST['form_id'] == "grader"){

}
else if($_REQUEST['form_id'] == "config_menu"){
    print getConfigurations();
}
else if($_REQUEST['form_id'] == ""){

}

// Returns all the configurations from the database file
function getConfigurations() {
    require("../_private/database.php");
    try {
        $results = $db->query("
                SELECT * FROM config");
    } catch (Exception $e) {
        echo "Data could not be retrieved from the database.";
        exit;
    }
    $recent = $results->fetchAll(PDO::FETCH_ASSOC);

    $config_list = "";
    $i = 0;

    foreach($recent as $value) {
        $i += 1;
        $config_list .= "<div id=\"config_form\">\n";
        $config_list .= "\t<form id=\"config_" . $i ."\" method=\"post\" action=\"controller.php\">\n";
        $config_list .= "\t<input type=\"hidden\" name=\"form_id\" value=\"config\">\n";
        $config_list .= "\t<input type=\"text\" name=\"setting\" value=\"" . $value["setting"] . "\" readonly>\n";
        $config_list .= "\t<input type=\"text\" name=\"value\" value=\"" . $value["value"] . "\">\n";
        $config_list .= "\t<button id=\"button_" . $i ."\">Save</button>\n";
        $config_list .= "\t<span id=\"saved_" . $i . "\" style=\"color: #f00;\"></span>\n";
        $config_list .= "\t</form>\n";
        $config_list .= "</div>\n";
    }

    return $config_list;
}


function setConfigurations($setting, $value) {
    require("../_private/database.php");
    try {
        $results = $db->query("
                UPDATE config SET value=\"" . $value . "\" WHERE setting = \"" . $setting . "\"");
    } catch (Exception $e) {
        echo "Data could not be written to the database.";
        exit;
    }
    return true;
}

?>

I hope this makes sense to someone. Thanks!

Ken Alger
Ken Alger
Treehouse Teacher

Edited for markdown.

Are there any errors in your JavaScript console (Opt-Cmd-C Mac, Alt-Ctrl-C Windows)?

Bill Dowd
Bill Dowd
7,681 Points

No. There are no errors. It just takes me to controller.php. I can even add a function call to display the data that I want and it will show, but I'm using a JQuery UI tabs script to display my pages inside of the tabs and that all goes away when I get sent to the controller.php page.

Can you include the code for the .setup_main_menu form?

Bill Dowd
Bill Dowd
7,681 Points

Hi Iain. Here is the code. Note: the bottom 4 forms, inside the div id=output is what was dynamically generated from a DB query inside a function used to build the forms.

<div id="" class="setup_menu_group">
    <form name="units" method="post" action="controller.php" class="setup_main_menu">
        <input type="hidden" name="form_id" value="units_menu">
        <button class="units">Units</button>
    </form>

    <form name="lessons" method="post" action="controller.php" class="setup_main_menu">
        <input type="hidden" name="form_id" value="lessons_menu">
        <button id="lessons">Lessons</button>
    </form>

    <form name="certificates" method="post" action="controller.php" class="setup_main_menu">
        <input type="hidden" name="form_id" value="certificates_menu">
        <button id="certificates">Certificates</button>
    </form>

    <form name="graders" method="post" action="controller.php" class="setup_main_menu">
        <input type="hidden" name="form_id" value="graders_menu">
        <button id="graders">Graders</button>
    </form>

    <form name="config" method="post" action="controller.php" class="setup_main_menu">
        <input type="hidden" name="form_id" value="config_menu">
        <button id="config">Config</button>
    </form>
</div>
<div id="output" class="">
    <input class="results_header" type="text" value="Setting" readonly>
    <input class="results_header" type="text" value="Value" readonly>
    <div id="config_form">
        <form id="config_1" method="post" action="controller.php" class="test">
            <input type="hidden" name="form_id" value="config">
            <input type="text" name="setting" value="birthday_alert_days" readonly>
            <input type="text" name="value" value="30">
            <button id="button_1">Save</button>
            <span id="saved_1" style="color: #f00;"></span>
        </form>
    </div>
    <div id="config_form">
        <form id="config_2" method="post" action="controller.php" class="test">
            <input type="hidden" name="form_id" value="config">
            <input type="text" name="setting" value="default_state" readonly>
            <input type="text" name="value" value="WA">
            <button id="button_2">Save</button>
            <span id="saved_2" style="color: #f00;"></span>
        </form>
    </div>
    <div id="config_form">
        <form id="config_3" method="post" action="controller.php" class="test">
            <input type="hidden" name="form_id" value="config">
            <input type="text" name="setting" value="mia_days" readonly>
            <input type="text" name="value" value="90">
            <button id="button_3">Save</button>
            <span id="saved_3" style="color: #f00;"></span>
        </form>
    </div>
    <div id="config_form">
        <form id="config_4" method="post" action="controller.php" class="test">
            <input type="hidden" name="form_id" value="config">
            <input type="text" name="setting" value="show_states" readonly>
            <input type="text" name="value" value="WA,AZ">
            <button id="button_4">Save</button>
            <span id="saved_4" style="color: #f00;"></span>
        </form>
    </div>
</div>

8 Answers

Bill Dowd
Bill Dowd
7,681 Points

From just a quick glance, it looks like you're loading the content for the forms in the setup area dynamically -- that is, the forms with data load via AJAX based on the buttons the user clicks. Is that the case? If so, you have to apply the "submit" either when the code new HTML is loaded, or use event delegation. With jQuery event delegation lets you attach an event handler to a parent element that's already on the page -- that parent element looks for events on particular children and then runs the event handler. The child element doesn't need to be in place beforehand, just the parent.

For example, say you had this code when the page loads:

<div id="formContainer">

</div>

then using Ajax you load something new into that div, so that the div then looks like this

<div id="formContainer">
  <form action="controller.php">

  </form>
</div>

You would use event delegation like this. First select the parent element

$('#formContainer')

than use the 'on' event, add the submit event, the child selecotr, and the function that should run when the event occurs on the child like this:

$('#formContainer').on('submit','form', function () {

});

Notice, the second argument to the on() function. This tells the parent -- formContainer -- that when a form inside it is submitted, then run the function.

Read this page for more information: http://learn.jquery.com/events/event-delegation/

Thank you Dave McFarland...

Ahh yes.

Joe Manson and I were both close with the .on() suggestions.

Glad you finally got this one sorted!

Bill Dowd
Bill Dowd
7,681 Points

That's correct Iain and I thank you both for your help. I'm still learning this stuff and it's very cool.

Bill

Joe Manson
Joe Manson
11,817 Points

Does it work if you just write the HTML instead of dynamically generating it with the PHP? If it does try making .submit(function ... be .on("submit", function ...

Bill Dowd
Bill Dowd
7,681 Points

Hi Joe,

I grabbed the dynamically generated form from the source code and pasted it into the div where all the results of the buttons go. The only thing that works in the JavaScript processor is $('form').submit(function(evt) { evt.preventDefault(); If I try on.("submit")(function(evt) { evt.preventDefault(); or anything else, it fails. Here is the interesting thing: after the page refresh the hard coded form is present and it works perfectly, but if I click the "config" button to show the same form in the div, it does not work. It takes me to the controller.php file in the address bar. I presume that the form is being overwritten with the results of the buttons above.

Okay so, first up, though it probably won't make a difference to the functioning of your code, you're repeating the id attribute value of config_form for each one of the divs surrounding the config forms. Change that to a class if it's used for styling purposes (in your PHP code).

Secondly, the value attributes of the form_id hidden input fields in your .setup_main_menu forms don't match what your PHP code is expecting for the value of the $_REQUEST['form_id'] variable, with the exception of config_menu.

And I have no idea whether this would affect it or not, but I don't think you should use existing attribute names in the name attribute... e.g. don't use name="value". Since value is a property of form elements in the DOM, and even val is a method from jQuery, I'd suggest something else. Maybe use config_setting and config_value or something like that?

I don't really know if changing anything I suggested is actually going to help, but it's probably good practice anyways!

Bill Dowd
Bill Dowd
7,681 Points

Thanks Iain. I've corrected my code, but as you predicted, it didn't make the form submission get halted by JQuery. As always, it did update the DB, but it sends me to the controller.php page.

I'm curious about something: Why when I view source on the page with the config form, the div where the form lives is empty, but it shows on the page in a normal view?

Bill

I don't think viewing the source code will show you anything that has been loaded via Ajax, which the outputted area would be, correct?

Anyways, the only other thing I can think of is that the submit event handlers aren't being properly bound to the .setup_main_menu forms...

Are those forms being generated dynamically with Ajax or anything similar? Perhaps you could change the code to use jQuery's .on() method?

You would use the following:

$('.setup_menu_group').on('submit', '.setup_main_menu', function(evt) { // This captures the menu buttons clicks
...

The .on() method is also supposedly more efficient that the .click()/.submit() etc. methods.

I'm running out of ideas though...

Bill Dowd
Bill Dowd
7,681 Points

Thanks for trying. I really appreciate your efforts. I'm trying to continue coding the program with this part not working, but it would be really nice to see it the way I had envisioned. Yes to your first question - that is where the data gets placed via AJAX, so I can expect to NOT see the code when I do a view source.

Can you tell me about how the handlers get bound, conditions that must be met so it might help me to troubleshoot and understand better?

I tried the .on() method and no difference. Does it matter that in the index.php, where the action is taking place, I'm using a menu tab widget? Index.php is where all the php pages get loaded - into their respective div's.

Yes, the form I'm having trouble with is dynamically generated via PHP. However, before I added the menu system for that "tab" it was working just fine.

Bill

Perhaps you should put some console.log() calls throughout your JS/jQuery to try and determine where it's going wrong.

That and add an error callback function to your Ajax (similar to the success one you've already got), to see if that's where it is failing...

This is getting a bit tricky to picture everything in my head, and I'm getting a bit lost with how you've got it all pieced together.

Not sure if there is a CodePen equivalent that allows sharing of PHP code, since sharing server side code involving databases is generally not a good idea...

Sorry I couldn't be of more help!

Bill Dowd
Bill Dowd
7,681 Points

Just to prove to me that the JavaScript was working, I grabbed the function from the controller.php and placed it in the index.php inside the "tab" for that page. (I'll include the entire index.php). It worked just as it had originally. It is under the last tab, id=setup, the rest is just demo text.

<?php

?>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
 <head>
  <title>PFC</title>
  <meta name="Generator" content="EditPlus">
  <meta name="Author" content="">
  <meta name="Keywords" content="">
  <meta name="Description" content="">
  <link rel="stylesheet" href="styles/jquery-ui.css">
<script src="js/jquery-1.11.1.js"></script>
<script src="js/jquery-ui.js"></script>
<script>
  $(function() {
    $( "#number" )
      .selectmenu()
      .selectmenu( "menuWidget" )
        .addClass( "overflow" );
  });
</script>

<style>
    label {
        display: block;
        margin: 30px 0 0 0;
        }
    select {
        width: 200px;
        }
    .overflow {
        height: 200px;
        }
    #mainheading {margin: 0 0 15px 0; padding: 0px 0px 0px 20px;}
    #mainheading p {font-size: 40px; padding-left: 50px; display: inline;}
    .subtitle {font: bold 40px "serif"; text-align: center; padding: 10px 0px 10px 0px;}
    #birthdays {margin: 0px; padding: 5px; border: solid 1px #000; background-color: #9cf;}
    #birthdays #title {font: italic bold 30px serif; padding: 0px 0px 5px 0px;}
    .vertspace {padding: 10px 0px;}
    #mia {margin: 0px; padding: 5px; border: solid 1px #000; background-color: #f66;}
    #mia #title {font: italic bold 30px serif; padding: 0px 0px 5px 0px;}
    #stats {margin: 0px; padding: 5px; border: solid 1px #000; background-color: #fc9;}
    #stats #title {font: italic bold 30px serif; padding: 0px 0px 5px 0px;}
    #setup_menu_background {width: 500px; height: 48px; solid 1px #000; background-color: #ccc;}
    .setup_menu_group {margin-left: auto; margin-right: auto; padding: 12px 10px; border-bottom: background-color: none;}
    .setup_main_menu {display: inline; padding: 12px 10px; background-color: #eee; border: solid 0px #000;}
    #config_output {margin-top: 20px;}
    .results_header {background: #36f; text-align: center;}
    #reports_menu_group {margin-left: auto; margin-right: auto; padding: 12px 10px; border-bottom: background-color: none;}
    .reports_menu {display: inline; padding: 12px 10px; background-color: #eee; border: solid 0px #000;}
    #report_output {margin-top: 20px;}
</style>
<script>
    $(document).ready(function() {

        // This captures the config results DB updates "Save" button
        $('.test').on('submit', function(event) { 
            event.preventDefault();
            var id = $(this).find("span").attr("id"); // Finds the span id attribute
            $('#' + id).show(); // Shows any faded out "Saved!" text
            var url = $(this).attr("action");
            var formData = $(this).serialize();
            $.post(url, formData, function(response) {
                $('#' + id).text("Saved!").fadeOut(3000);
            }); // end post
        }); // end submit


        // This captures the reports menu button clicks
        $('#reports_menu_group').submit(function(event) { 
            event.preventDefault();
            var url = $(this).attr("action");
            var formData = $(this).serialize();
            $.ajax(url, {
                data : formData,
                type : "POST",
                success : function(response) {
                    $('#report_output').html(response);
                }
            }); // end ajax
        }); // end submit


        // This captures the config menu button clicks
        $('.setup_main_menu').submit(function(event) { 
            event.preventDefault();
            var url = $(this).attr("action");
            var formData = $(this).serialize();
            $.ajax(url, {
                data : formData,
                type : "POST",
                success : function(response) {
                    $('#config_output').html(response);
                }
            }); // end ajax
        }); // end submit

    }); // end ready
</script>

</head>

<body>
<div id="mainheading" class="">
    <img src="images/pfc-logo.png" width="137" height="141" border="0" alt="PFC Logo">
    <p>PFC Lesson Tracking</p>
  </div>

<div id="tabs">

  <ul>
    <li><a href="#overview"><span>Overview</span></a></li>
    <li><a href="#inmates"><span>Inmates</span></a></li>
    <li><a href="#reports"><span>Reports</span></a></li>
     <li><a href="#setup"><span>Setup</span></a></li>
 </ul>

<!-- Overview Tab Contents -->
    <div id="overview">
        <div class="subtitle">Overview</div>
        <div id="birthdays" class="">
            <div id="title">Birthdays</div>
            <div>Displays a list of upcoming birthdays with the critera set by the configuration under "Setup."</div>
            <ul>
                <li>Richard Foster: October 25</li>
                <li>Dallas Willard: November 2</li>
                <li>Matt Hannan: November 17</li>
            </ul>
        </div>

    <div class="vertspace"></div>

    <div id="mia" class="">
        <div id="title">Missing in Action</div>
        <div>Displays a list of those whom have not been heard from since the critera set by the configuration under "Setup."</div>
        <ul>
            <li>Bubba Smith: 122 Days</li>
            <li>Chuck Buck: 108 Days</li>
            <li>Big Al Marconii: 95 Days</li>
        </ul>
    </div>

    <div class="vertspace"></div>

    <div id="stats" class="">
        <div id="title">Stats</div>
        <div>Displays preferred statistics. (May be unnecessary)</div>
            <ul>
                <li>Active Students: 461</li>
                <li>Completed All Units & Lessons: 429</li>
                <li>Total Students Served: 890</li>
            </ul>
        </div>
    </div>

<!-- Inmates Tab Contents -->
    <div id="inmates">
        <p>Date Sent: <input type="text" id="datepicker"></p>
        <script>
        $( "#datepicker" ).datepicker({
            changeMonth: false,
            changeYear: false,
            showOtherMonths: true,
            selectOtherMonths: false,
            numberOfMonths: [ 1, 3 ],
            showCurrentAtPos: 1,
            showOn: "both",
            buttonImage: "images/calendar.gif",
            buttonImageOnly: true
              });
        $( "#datepicker2" ).datepicker({ altField: "#actualDate" });
        </script>
    </div>

<!-- Reports Tab Contents -->
    <div id="reports">
        <?php include_once("reports.php"); ?>
    </div>


<!-- Setup Tab Contents -->
    <div id="setup">
        <?php
        function getConfigurations() {
    require("../_private/database.php");
    try {
        $results = $db->query("
                SELECT * FROM config");
    } catch (Exception $e) {
        echo "Data could not be retrieved from the database.";
        exit;
    }
    $recent = $results->fetchAll(PDO::FETCH_ASSOC);

    $config_list = "<input class=\"results_header\" type=\"text\" value=\"Setting\" readonly>\n";
    $config_list .= "<input class=\"results_header\" type=\"text\" value=\"Value\" readonly>\n";
    $i = 0;

    foreach($recent as $value) {
        $i += 1;
        $config_list .= "<div id=\"config_form_" . $i . "\">\n";
        $config_list .= "\t<form id=\"config_" . $i ."\" method=\"post\" action=\"controller.php\" class=\"test\">\n";
        $config_list .= "\t\t<input type=\"hidden\" name=\"form_id\" value=\"config\">\n";
        $config_list .= "\t\t<input type=\"hidden\" name=\"query_type\" value=\"update\">\n";
        $config_list .= "\t\t<input type=\"text\" name=\"config_setting\" value=\"" . $value["setting"] . "\" readonly>\n";
        $config_list .= "\t\t<input type=\"text\" name=\"config_value\" value=\"" . $value["value"] . "\">\n";
        $config_list .= "\t\t<button id=\"button_" . $i ."\">Save</button>\n";
        $config_list .= "\t\t<span id=\"saved_" . $i . "\" style=\"color: #f00;\"></span>\n";
        $config_list .= "\t</form>\n";
        $config_list .= "</div>\n";
    }

    return $config_list;
}

print getConfigurations();

?>
    </div>
</div>

<!-- Script to execute the Tabs -->
 <script>
    $( "#tabs" ).tabs();
</script>


</body>
</html>
Bill Dowd
Bill Dowd
7,681 Points

So this is what I wonder: the forms are now visible when I view source and they're working. However, when I put the menus for that "tab" back and the results place these forms in the output div and I can't see them when I view source, that is when they are not working.

Does the fact that they are "hidden" in the output div keep them from functioning correctly?

If you mean hidden by CSS, then no, there shouldn't be any difference...

This all seems like some of the jQuery or Ajax is modifying HTML and you're either losing the event handlers that are bound to some of the modified elements, or some JavaScript is getting run too early or to late...

You might just have to systematically add in JS console.log() and PHP echo/var_dump() commands everywhere to see what is and isn't being run, and in what order.

Bill Dowd
Bill Dowd
7,681 Points

My terminology may be wrong, but I would say no, not hidden by CSS. It's the results of the button-push that places the results of the DB query, formatted into a form for each record, into that output div on the page. You know, the one that I can't see the html when I view source?

I will experiment with the console.log() and see if I can find out anything.

Thanks.

Dave McFarland
STAFF
Dave McFarland
Treehouse Teacher

Hi Bill Dowd

Is this somewhere live that we can see it? Can you provide a URL? It's difficult to troubleshoot with just these snippets. If it's up on a web server it'll be a lot easier to try to figure out an answer.

Bill Dowd
Bill Dowd
7,681 Points

Hi Dave, It's running on my laptop using WAMP, but I could place it on one of my Websites. Give me a day or so to get that done and I'll send you the URL.

Alternatively, you could use something like Cod.io, an online IDE.

Here's a guide to setting up PHP on their cloud servers.

Their free plans allow you to create unlimited public, open source projects, so it's well suited to sharing some example code you're working on, but obviously you don't want to share the code you're using for production/live sites (particularly the production database and connection details).

Anyways, that would allow us to access and fork your code, and see the issues you're having.

Dave McFarland
STAFF
Dave McFarland
Treehouse Teacher

To begin troubleshooting this Bill Dowd,

I'd first strip your HTML and JavaScript code down to the smallest testable unit:

  1. Create a web page with JUST the html form you have had trouble with -- it sounds like it's just the one.
  2. Validate the HTML to make sure it's valid HTML -- the markup you posted didn't pass W3C validation when I tested it.
  3. Then add the JavaScript that responds to just that one form.
  4. Strip everything out of the Javascript except the original event handler:
$('#config_1').submit(function(evt) { // This captures the config results DB updates "Save" button
            evt.preventDefault();
}); // end submit

The code at this point should do nothing -- not submit to the controller.php file at all. Then slowly add one line of JS at a time and test. Repeat this process until you can identify the error.

For us to help, however, you'll need to place this up on a publicly accessible web server -- it sounds like there are too many moving parts for us to decipher the problem with just the code you've provided. If, as you suggest, the problem is related to the tabbed panels, we'll need to see a live demo to figure this out.