new to forum, AJAX and JQuery. A little experience of PHP and JS.
I’m trying to present a long series of questions (400+) one by one in an input text field on a form with 2 submit buttons labelled “True” and “False”. I need one question to be presented at a time, then record the True or False result as (1 or -1) sequentially into another text file. I cannot refresh the input field with the next question after ‘Submit’. I believe that AJAX would be the answer. This code is the first effort: (any later efforts are more complicated, but don’t work any better) it opens the questions file (CPXQ.dat) into an indexed array, then places the first question into the input text field. When either of the submit buttons are pressed, the result is POSTed to data.cpx, and the next question appears, but it won’t continue thereafter. I have tried various PHP loops and some javascript, but these don’t work, either looping through immediately to the last question, or getting stuck in the loop. (The php includes just contain CSS and JQuery source.) I’d also like to prevent the user from being able to go back over any of the questions, but that may be a query for another day!
Any advice much appreciated, and apologies if not clear. Happy to provide any further info.
<div class="container"> <?php include("top.php"); ?> <div class="intro"> <p><h1>CPI TEST</h1></p> <?php $i = 0; //file in to an array $lines = file("CPXQ.dat"); ?> <?php if(isset($_POST['submitT'])) { //echo $_POST['submitT']; $data="1"; //echo $data; $fp = fopen('data.cpx', 'a') or die("Unable to open file!"); fwrite($fp, PHP_EOL); fwrite($fp, $data); fclose($fp); ++$i; } if(isset($_POST['submitF'])) { //echo $_POST['submitF']; $data="-1"; //echo $data; $fp = fopen('data.cpx', 'a') or die("Unable to open file!"); fwrite($fp, PHP_EOL); fwrite($fp, $data); fclose($fp); ++$i; } ?> <form method = "post" action = "CPI_Test.php"> <input type="text" name="question" value="<?php echo $lines[$i];?>"> <input type="submit" name="submitT" value="True"> <input type="submit" name="submitF" value="False"> </form> </div> </body>
Here’s the code for the preliminary page collecting user details:
<!DOCTYPE html> <html> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>PPCS CPI Information</title> <?php include("head.php"); ?> </head> <body> <?php // define variables and set to empty values $initdataErr = $surnamedataErr = $agedataErr = $gendataErr = ""; $initdata = $surnamedata = $agedata = $gendata = $codata = ""; function test_input(&$surnamedata) { $surnamedata = trim($surnamedata); $surnamedata = stripslashes($surnamedata); //$data = htmlspecialchars($data); $surnamedata = preg_replace('/s+/', '', $surnamedata); return $surnamedata; } if ($_SERVER["REQUEST_METHOD"] == "POST") { if (empty($_POST['initdata'])) { $initdataErr = "Initials are required"; } else { $initdata = test_input($_POST['initdata']); // check if name only contains letters and whitespace if (!preg_match("/^[A-Z- ]*$/",$initdata)) { $initdataErr = "Please use capital letters without spaces only"; } } if (empty($_POST['surnamedata'])) { $surnamedataErr = "Surname is required"; } else { $surnamedata = test_input($_POST['surnamedata']); // check if name only contains letters and whitespace if (!preg_match("/^[a-zA-Z-' ]*$/",$surnamedata)) { $surnamedataErr = "Please use letters only"; } } if (empty($_POST['agedata'])) { $agedataErr = "Age is required"; } else { $agedata = test_input($_POST['agedata']); // check if name only contains letters and whitespace if (!preg_match("/^[0-9]*$/",$agedata)) { $agedataErr = "Only numbers and white space allowed"; } } if (empty($_POST['gendata'])) { $gendataErr = "Gender is required"; } } ?> <div class="container"> <?php include("top.php"); ?> <br><h1>CPI TEST INFORMATION</h1><br> <b>Please fill in the form below carefully</b> <p><span class="error">* required field</span></p> <br> <form method="post" action="<?php echo htmlspecialchars($_SERVER["PHP_SELF"]);?>"> Initials: <span class="error">* <?php echo $initdataErr;?></span> <br> <input type="text" name="initdata" value="<?php echo $initdata;?>"><br> <br> Surname: <span class="error">* <?php echo $surnamedataErr;?></span> <br> <input type="text" name="surnamedata" value="<?php echo $surnamedata;?>" ><br> <br> Company (Optional):<br> <input type="text" name="codata" value="<?php echo isset($_POST["codata"]) ? $_POST["codata"] : '';?>" ><br> <br> Age in Years: <span class="error">* <?php echo $agedataErr;?></span><br> <input type="text" name="agedata" maxlength="2" min="0" max="99" step="1" pattern="[0-9]{2}"value="<?php echo $agedata;?>"><br> <br> Gender: <span class="error">* <?php echo $gendataErr;?></span><br> <select name="gendata"> <option value="">Select...</option> <option value="m" <?php echo (isset($_POST['gendata']) && $_POST['gendata'] == 'm') ? 'selected' : ''; ?>>Male</option> <option value="f" <?php echo (isset($_POST['gendata']) && $_POST['gendata'] == 'f') ? 'selected' : ''; ?>>Female</option> </select> <br> <input type="submit" name="submit" value="Submit"> </form> </div> </body> </html> <?php if(isset($_POST['submit'])){ // Fetching variables of the form which travels in URL $initdata = $_POST['initdata']; $surnamedata = $_POST['surnamedata']; $codata = $_POST['codata']; $agedata = $_POST['agedata']; $gendata = $_POST['gendata']; if($initdata !=''&&(preg_match("/^[A-Z]*$/",$initdata)) && $surnamedata !='' && (preg_match("/^([A-Za-z -]+(?:'|�*39;)*)*[A-Za-z -]+$/",$surnamedata)) && $agedata !='' && (preg_match("/^[0-9]*$/",$agedata)) && $gendata !='') { date_default_timezone_set("Europe/London"); //^['a-zA-Z]*$/ This is the most recent test_input($surnamedata); $_POST['surnamedata'] = ucwords($_POST['surnamedata']); $data = '"' . $_POST['initdata'] . ' ' . stripslashes($_POST['surnamedata']) . '","' . $_POST['agedata'] . '","'. $_POST['gendata'] .'","' . $_POST['codata'] . '","' . '","'. '","'. date("d/m/Y"). '","'. date("H:i:s"). '","'; //Create CPX filename $fn = $_POST['initdata'] . $_POST['surnamedata']; $fn = preg_replace('/PL/u', '', $fn); $fn = strtoupper($fn); $fn = $fn . "XXXXXX"; $fn = substr($fn,0,8); echo "$fn"; echo "$data"; //Create temp file for CPX filename $fp = fopen($fn . '.temp', 'a') or die("Unable to open file!"); fwrite($fp, $fn); fclose($fp); //Create CPX file $fp = fopen($fn . '.cpx', 'a') or die("Unable to open file!"); fwrite($fp, $data); //Append new line //fwrite($fp, "ntest"); fclose($fp); // Redirect /header("Location:/CPI_Form_Trial/instructions.php"); } else{ ?> <br><span class = "error"><?php echo "Please make sure that you have filled in all required fields and click 'Submit' again";?></span> <?php } } ?>
Advertisement
Answer
Using AJAX (fetch) is ideally suited to this type of problem where you do not wish to refresh the screen and want to present new data after submitting a http request. The following single page application shows how you might accomplish your stated goal but it does not take into account a couple of possible issues which are:
[a] multiple users participating in the questionnaire simultaneously [b] a user abandoning the questionnaire and restarting, once or more than once.
The issues mentioned could be negated by using a database to store answers and assigning the users unique identifiers (ie: user id, username) which is used when sending the ajax request.
The demo that follows will write, to the answerfile, either 1 or 0 ( which is more common than -1 for false ) alongside the question line number ( which is sort of the ID )
<?php if( $_SERVER['REQUEST_METHOD']=='POST' && isset( $_POST['action'] ) ){ ob_clean(); $file='CPXQ.dat'; $answerfile='data.cpx'; $lines=file( $file ); switch( $_POST['action'] ){ case 'start': header('Content-Type: text/html'); exit( $lines[0] ); break; case 'question': $id=(int)$_POST['id']; # increment ID value $id++; $question=isset( $lines[ $id ] ) ? $lines[ $id ] : false; if( $question && $id < count( $lines ) ){ # log the current answer file_put_contents( $answerfile, sprintf( 'q:%s,a:%s', $id, $_POST['answer'] ) . PHP_EOL, FILE_APPEND ); # the json payload $data=array( 'id' => $id, 'question' => $question ); } elseif( !$question && $id==count( $lines ) ){ # log the final answer file_put_contents( $answerfile, sprintf( 'q:%s,a:%s', $id, $_POST['answer'] ) . PHP_EOL, FILE_APPEND ); $data=array( 'id' => 0, 'question' => 'End of questionnaire' ); } header('Content-Type: application/json'); exit( json_encode( $data ) ); break; } exit(); } ?> <!DOCTYPE html> <html lang='en'> <head> <meta charset='utf-8' /> <title>Questions, questions, questions...</title> </head> <body> <!-- a basic form: The question is rendered within the `fieldset` element. Both buttons use datasets, the data-id refers to the question number (line from source file) and data-value is the boolean value indicating answer value. --> <form name='questions' method='post'> <fieldset></fieldset> <div> <input type='button' data-value=1 data-id=0 value='True' /> <input type='button' data-value=0 data-id=0 value='False' /> </div> </form> <script> const fs=document.querySelector('fieldset'); /* When the page loads we fetch the first question ( ie: first line in source file ) and display on the page. The action parameter helps the backend determine what stage we are at. */ let fd=new FormData(); fd.set('action', 'start'); // make the request an show question fetch( location.href, { method:'post', body:fd }) .then(r=>r.text()) .then(text=>{ fs.innerHTML=text; }); /* The delegated event handler processes button clicks and sends the data-id ( ie: line number ) and answer to the backend processing script. Here this is all done on the same page for convenience - for your case it would be CPI_Test.php */ const clickhandler=function(e){ if( e.target.tagName.toLowerCase()=='input' && e.target.type=='button' ){ // set a different `action` so that the backend knows what to do fd.set('action','question'); fd.set('id',e.target.dataset.id); fd.set('answer',e.target.dataset.value); // send the request and display new question fetch( location.href, { method:'post', body:fd } ) .then( r=>r.json() ) .then( json=>{ fs.innerHTML=json.question; // update the buttons so that they have the new id assigned ready for next click // or disable ( or remove ) when the questionnaire is over. document.querySelectorAll('[type="button"][data-value]').forEach( bttn=>{ if( Number( json.id ) !==0 ) bttn.dataset.id=json.id; else bttn.disabled=true; }); }) } }; // add a delegated event handler to process button clicks document.forms.questions.addEventListener( 'click', clickhandler ); </script> </body> </html>
A sample of the answerfile:
q:1, a:1 q:2, a:0 q:3, a:1 q:4, a:1 q:5, a:0 q:6, a:0 q:7, a:1 q:8, a:0 q:9, a:1 q:10, a:0 q:11, a:1 q:12, a:1 q:13, a:1 q:14, a:1 q:15, a:0 q:16, a:1 q:17, a:0 q:18, a:1 q:19, a:1 q:20, a:0
I hope it helps you arrive at a solution but as mentioned it would be more robust / reliable with a database rather than simple text file.