可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
Select
based on another select
using PHP
not working only when a new row is added.
For demo visit here (See steps for demo)
- There are two rows when the page is loaded, they are working as wanted. (Service option is as per item option)
- Click on the Add Row button, one more row will be added
- The newly inserted row is not working. (Service select is not giving any options, as first two gives)
What I tried:
<?php require_once '../home.php' ?> <?php if( $_SERVER['REQUEST_METHOD']=='POST' && isset( $_POST['action'], $_POST['id'] ) && $_POST['action']=='get_dependant_menu' ){ ob_clean(); $action=filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING ); $id=filter_input( INPUT_POST, 'id', FILTER_SANITIZE_STRING ); if( $action && $id && !is_nan( $id ) ){ $stmt=$user_home->runQuery("SELECT * FROM service WHERE IRN=:irn ORDER BY Sr ASC "); $stmt->bindParam(':irn',$id); $stmt->execute(); $stmtin=$user_home->runQuery("SELECT * FROM item WHERE IRN=:irn ORDER BY Sr ASC "); $stmtin->bindParam(':irn',$id); $stmtin->execute(); $rowin=$stmtin->fetch( PDO::FETCH_ASSOC ); if( $stmt->rowCount() > 0 ){ echo "<option value='Select Service'>Select Service ({$rowin['Name']})</option>"; while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ echo "<option value='{$row['SRN']}'>{$row['Name']}</option>"; } } } exit(); } ?> <!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> <script type='text/javascript' charset='utf-8'> function ajax(m,u,p,c,o){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if( xhr.readyState==4 && xhr.status==200 )c.call( this, xhr.response, o, xhr.getAllResponseHeaders() ); }; var params=[]; for( var n in p )params.push(n+'='+p[n]); switch( m.toLowerCase() ){ case 'post': p=params.join('&'); break; case 'get': u+='?'+params.join('&'); p=null; break; } xhr.open( m.toUpperCase(), u, true ); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send( p ); } function createmenu(r,o,h){ o.menu.innerHTML=r; } function bindEvents(){ var oSelItem=document.querySelector('select[name="item1"]'); var oSelService=document.querySelector('select[name="service1"]'); oSelItem.onchange=function(e){ var method='post'; var url=location.href; var params={ 'action':'get_dependant_menu', 'id':this.options[ this.options.selectedIndex ].value }; var opts={ menu:oSelService }; ajax.call( this, method, url, params, createmenu, opts ); }.bind( oSelItem ); } document.addEventListener( 'DOMContentLoaded', bindEvents,false ); </script> </head> <body> <form method="post" action="invoice_form.php" id="item_sel"> <table id="chiru_inv" class="table table-striped table-hover table-bordered table-responsive"> <tr> <td colspan="3"> <input type="text" name="customer" value="" placeholder="Customer Name"> </td> </tr> <tr> <th>Item</th> <th>Service</th> <th>Qty</th> </tr> <tr> <td> <select name='item1' class='country'> <option value="Select Item">Select Item</option> <?php //$sql='select * from `item` order by `Sr` asc;'; $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); $stmt->execute(); if( $stmt->rowCount() > 0 ){ while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; } } ?> </select></td> <td><select class="country" name="service1"> </select></td> <td><input type="text" name="qty1" value="" placeholder="Quantity"></td> </tr> <tr> <td> <select name='item2' class='country'> <option>Select Item</option> <?php $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); $stmt->execute(); if( $stmt->rowCount() > 0 ){ while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; } } ?> </select></td> <td><select class="country" name="service2"> </select></td> <td><input type="text" name="qty2" value="" placeholder="Quantity"></td> </tr> <tr> <td colspan="3"><button type="submit" name="btnsave" class="btn btn-default"> <span class="glyphicon glyphicon-save"></span> Save </button> </td> </tr> </table> <input type="button" class="add-row" value="Add Row"> </form> <div id="markup_model" class="hide"> <table> <tr> <td> <select name="nameitem" class="country"> <option>Select Item</option> <?php $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); $stmt->execute(); if( $stmt->rowCount() > 0 ){ while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; } } ?> </select> </td> <td> <select class="country" name="namewhat"></select> </td> <td> <input type="text" name="nameqty" value="" placeholder="Quantity" /> </td> </tr> </table> </div> </div> <script src="https://code.jquery.com/jquery-1.12.4.min.js"></script> <script type="text/javascript"> $(document).ready(function() { var cont = 3 var qty = "qty" var item = "item" var what = "service" $(".add-row").click(function() { var nameqty = qty + cont; var nameitem = item + cont; var namewhat = what + cont; var markup = $('#markup_model tbody'); $(markup).find('.country:eq(0)').attr('name', nameitem); $(markup).find('.country:eq(1)').attr('name', namewhat); $(markup).find('input').attr('name', nameqty); $(markup.html()).insertBefore($('button[type="submit"]').closest("tr")); cont++; }); }); </script> <script type='text/javascript' charset='utf-8'> function ajax(m,u,p,c,o){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if( xhr.readyState==4 && xhr.status==200 )c.call( this, xhr.response, o, xhr.getAllResponseHeaders() ); }; var params=[]; for( var n in p )params.push(n+'='+p[n]); switch( m.toLowerCase() ){ case 'post': p=params.join('&'); break; case 'get': u+='?'+params.join('&'); p=null; break; } xhr.open( m.toUpperCase(), u, true ); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send( p ); } function createmenu(r,o,h){ o.menu.innerHTML=r; } function bindEvents(){ var oSelItem2=document.querySelector('select[name="item2"]'); var oSelService2=document.querySelector('select[name="service2"]'); oSelItem2.onchange=function(e){ var method='post'; var url=location.href; var params={ 'action':'get_dependant_menu', 'id':this.options[ this.options.selectedIndex ].value }; var opts={ menu:oSelService2 }; ajax.call( this, method, url, params, createmenu, opts ); }.bind( oSelItem2 ); } document.addEventListener( 'DOMContentLoaded', bindEvents,false ); </script> <script type='text/javascript' charset='utf-8'> function ajax(m,u,p,c,o){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if( xhr.readyState==4 && xhr.status==200 )c.call( this, xhr.response, o, xhr.getAllResponseHeaders() ); }; var params=[]; for( var n in p )params.push(n+'='+p[n]); switch( m.toLowerCase() ){ case 'post': p=params.join('&'); break; case 'get': u+='?'+params.join('&'); p=null; break; } xhr.open( m.toUpperCase(), u, true ); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send( p ); } function createmenu(r,o,h){ o.menu.innerHTML=r; } function bindEvents(){ var oSelItem3=document.querySelector('select[name="item3"]'); var oSelService3=document.querySelector('select[name="service3"]'); oSelItem3.onchange=function(e){ var method='post'; var url=location.href; var params={ 'action':'get_dependant_menu', 'id':this.options[ this.options.selectedIndex ].value }; var opts={ menu:oSelService3 }; ajax.call( this, method, url, params, createmenu, opts ); }.bind( oSelItem3 ); } document.addEventListener( 'DOMContentLoaded', bindEvents,false ); </script> </body> </html>
回答1:
In your code above you are running the same query twice which I presume is a copy/paste mistake?
What follows is based upon previous question and help already offered.
mysql> describe irn_item; +-------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------+------------------+------+-----+---------+----------------+ | irn | int(10) unsigned | NO | PRI | NULL | auto_increment | | name | varchar(50) | YES | | NULL | | +-------+------------------+------+-----+---------+----------------+ mysql> select * from irn_item; +-----+------------+ | irn | name | +-----+------------+ | 1 | Shirt | | 2 | Trousers | | 3 | Jacket | | 4 | Socks | | 5 | Underpants | | 6 | Hat | +-----+------------+ mysql> describe irn_service; +-------------+------------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +-------------+------------------+------+-----+---------+----------------+ | srn | int(10) unsigned | NO | PRI | NULL | auto_increment | | irn | int(10) unsigned | NO | MUL | 1 | | | instruction | varchar(50) | NO | | 1 | | +-------------+------------------+------+-----+---------+----------------+ mysql> select * from irn_service; +-----+-----+-----------------------+ | srn | irn | instruction | +-----+-----+-----------------------+ | 1 | 2 | Iron | | 2 | 1 | Dry Clean Only | | 3 | 3 | Hi-Pressure dry clean | | 4 | 4 | Steam Clean | | 5 | 5 | Decontaminate | | 6 | 6 | Waterproof | +-----+-----+-----------------------+
Without knowing the schema of the database I made up a quick example database based upon some of the columns seen in the sql in the original code. The code below uses mysqli
rather than PDO simply because for my testing it is much quicker to write so please ignore the fact that it does not follow exactly. The important aspect that you are having troubles with, I believe, is that newly added rows do not copy the event handler which triggers the ajax request. I did make mention yesterday that when cloning nodes any event handlers assigned using addEventListener
will NOT be copied/cloned - so to ensure that event handlers are copied/cloned you need to use inline event handlers
~ ie: <select onchange='evtselect(event)'>...</select>
<?php $dbhost = 'localhost'; $dbuser = 'root'; $dbpwd = 'xxx'; $dbname = 'xxx'; $db = new mysqli( $dbhost, $dbuser, $dbpwd, $dbname ); if( $_SERVER['REQUEST_METHOD']=='POST' && isset( $_POST['action'], $_POST['id'] ) && $_POST['action']=='get_dependant_menu' ){ ob_clean(); try{ $id=filter_input( INPUT_POST, 'id', FILTER_SANITIZE_STRING ); if( $id && !empty( $id ) ){ $html=array(); $sql='select `srn`,`instruction` from `irn_service` where `irn` = ? order by `srn` asc'; $stmt=$db->prepare( $sql ); if( $stmt ){ $stmt->bind_param( 's', $id ); $stmt->execute(); $stmt->store_result(); $stmt->bind_result( $srn, $instruction ); while( $stmt->fetch() ){ $html[]="<option value='{$srn}'>{$instruction}"; } $stmt->close(); } header('Content-Type: text/html'); echo implode( PHP_EOL, $html ); } }catch( Exception $e ){ echo $e->getMessage(); } exit(); } ?> <!doctype html> <html> <head> <title>Dependent / Chained SELECT menus</title> <script> /* AJAX FUNCTION */ function ajax(m,u,p,c,o){ var xhr=new XMLHttpRequest(); xhr.onreadystatechange=function(){ if( xhr.readyState==4 && xhr.status==200 )c.call( this, xhr.response, o, xhr.getAllResponseHeaders() ); }; var params=[]; for( var n in p )params.push(n+'='+p[n]); switch( m.toLowerCase() ){ case 'post': p=params.join('&'); break; case 'get': u+='?'+params.join('&'); p=null; break; } xhr.open( m.toUpperCase(), u, true ); xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded'); xhr.send( p ); } /* AJAX CALLBACK */ function createmenu(r,o,h){ o.menu.innerHTML=r; } /* UTILITY TO FIND NEXT SIBLING ELEMENT NODE */ function get_nextsibling(n){ x=n.nextSibling; while ( x.nodeType!==1 ) x=x.nextSibling; return x; } /* INLINE EVENT HANDLER */ function evtselect(e){ try{ var el=e.target; if( el.value=='null' || el.value==null )return false; var method='post'; var url=location.href; var params={ 'action':'get_dependant_menu', 'id':el.value }; var td=get_nextsibling( el.parentNode ); var oSelect=td.querySelector('select'); var opts={ menu:oSelect }; ajax.call( this, method, url, params, createmenu, opts ); }catch( err ){ console.log( err ); } } function bindEvents(){ var bttn=document.querySelector('input[name="add"]'); var tbl=document.querySelector('form#item_sel > table'); if( bttn && tbl ){ bttn.addEventListener('click',function(e){ /* get a reference to the first & last row, of class "item", in table */ var tr=tbl.querySelectorAll( 'tr.items' )[0]; var ref=tbl.querySelector( 'tr.save' ); /* Create a clone of the entire row - which includes the inline event handlers */ var clone=tr.cloneNode( true ); /* Insert the new row after the last row */ tr.parentNode.insertBefore( clone, ref ); /* Ensure that newly added "service" select menu is empty */ clone.querySelector('select[name="item[]"]').value='null'; clone.querySelector('select[name="service[]"]').innerHTML=''; clone.querySelector('input[name="qty[]"]').value=''; },{ capture:false, passive:true, once:false } ); } } document.addEventListener( 'DOMContentLoaded', bindEvents, false ); </script> <style type='text/css' charset='utf-8'> select {padding:1rem;width:300px;} </style> </head> <body> <h1>Chained select menus using basic ajax</h1> <form method='post' id='item_sel'> <table> <tr class='headers'> <th scope='col'>Item</th> <th scope='col'>Service</th> <th scope='col'>Qty</th> </tr> <tr class='items'> <td> <select name='item' class='country' onchange='evtselect(event)'> <option value=null>Please Select <?php $sql='select * from `irn_item` order by `irn`;'; $result=$db->query( $sql ); $html=array(); if( $result ){ while( $rs=$result->fetch_object() ){ $html[]="<option value='{$rs->irn}'>{$rs->name}"; } echo implode( PHP_EOL, $html ); } ?> </select> </td> <td><select name='service' class='country'></select></td> <td><input type='number' name='qty' min=0 max=1000 /></td> </tr> <tr class='save'> <td colspan='3'> <button type='submit' name='btnsave' class='btn btn-default'> <span class='glyphicon glyphicon-save'></span> Save </button> </td> </tr> </table> <input name='add' type='button' class='add-row' value='Add Row' /> </form> </body> </html>
With this code I took the trouble of renaming newly added elements - this is probably not strictly necessary as you could instead use array syntax
for the element names - viz: <select name='item[]' onchange='evtselect(event)>
or <input type='number' name='qty[]' />
etc then access the appropriate values in PHP in the usual manner. It should be mentioned that if you decide upon that approach (ie array syntax ) then you would need to look at the various selectors used in the javascript and edit the names appropriately.
I trust that with this new insight into the use of inline event handlers
that you should be able to nail the problem you have - you can, with a little minor editing of this code, run it for yourself to see the application working correctly. You should edit the table names (irn_item
-> item
, irn_service
-> service
) and add suitable db/pwd details... Good luck
回答2:
You've really to change the logic here, I'm sorry but dealing with dynamic content statically will not work in this case... what if we have line 4 and 5 and 10.. you need really to give all set of elements a common class then attach the event using the event delegation on()
so the new elements added dynamically will also binded.
Example, change country
class to item
in the item columns, and country
class to service
in the service column :
$('body').on('change', '.item', function(){ //You're ajax logic here //Try to use `$.post()` instead //Target the related service list like var related_service_select = $(this).closest('tr').find('.service'); //Now you can fill it with the server side response });
Hope this helps.
回答3:
Your problem is because of this error - Uncaught TypeError: Cannot set property 'onchange' of null at HTMLDocument.bindEvents on line 243. You can see the same in inspect elements. This is because you are defining the function bindEvents for item3, which has not yet been added. Also this will work only in case of a single row being added. Changed your logic and used jquery a lot. I have commented out php part in your code and hard coded that stuff. Also created a php file that sends ajax response on select box change.
<?php // require_once '../home.php' ?> <?php foreach($_POST as $key => $val) { echo "$key => $val <br>"; } if( $_SERVER['REQUEST_METHOD']=='POST' && isset( $_POST['action'], $_POST['id'] ) && $_POST['action']=='get_dependant_menu' ){ echo "here <br>"; ob_clean(); // // $action=filter_input( INPUT_POST, 'action', FILTER_SANITIZE_STRING ); // $id=filter_input( INPUT_POST, 'id', FILTER_SANITIZE_STRING ); // if( $action && $id && !is_nan( $id ) ){ // // $stmt=$user_home->runQuery("SELECT * FROM service WHERE IRN=:irn ORDER BY Sr ASC "); // $stmt->bindParam(':irn',$id); // $stmt->execute(); // $stmtin=$user_home->runQuery("SELECT * FROM item WHERE IRN=:irn ORDER BY Sr ASC "); // $stmtin->bindParam(':irn',$id); // $stmtin->execute(); // $rowin=$stmtin->fetch( PDO::FETCH_ASSOC ); // // if( $stmt->rowCount() > 0 ){ // echo "<option value='Select Service'>Select Service ({$rowin['Name']})</option>"; // while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ // echo "<option value='{$row['SRN']}'>{$row['Name']}</option>"; // } // } // } echo "<option value='Select Service'>Select Service (Pant)</option>"; echo "<option value='12121211'>Iron</option>"; exit(); } ?> <!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> </head> <body> <div class="container"> <form method="post" action="invoice_form.php" id="item_sel"> <table id="chiru_inv" class="table table-striped table-hover table-bordered table-responsive"> <tr> <td colspan="3"> <input type="text" name="customer" value="" placeholder="Customer Name"> </td> </tr> <tr> <th>Item</th> <th>Service</th> <th>Qty</th> </tr> <tr> <td> <select name='item1' class='country'> <option value="Select Item">Select Item</option> <?php // //$sql='select * from `item` order by `Sr` asc;'; // $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); // $stmt->execute(); // // if( $stmt->rowCount() > 0 ){ // while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ // echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; // } // } ?> <option value='ZZ2017TF11A1'>Shirt</option> <option value='ZZ2017TF11A2'>Pant</option> </select></td> <td><select class="country" name="service1"> </select></td> <td><input type="text" name="qty1" value="" placeholder="Quantity"></td> </tr> <tr> <td> <select name='item2' class='country'> <option>Select Item</option> <?php // // $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); // $stmt->execute(); // // if( $stmt->rowCount() > 0 ){ // while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ // echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; // } // } ?> <option value='ZZ2017TF11A1'>Shirt</option> <option value='ZZ2017TF11A2'>Pant</option> </select></td> <td><select class="country" name="service2"> </select></td> <td><input type="text" name="qty2" value="" placeholder="Quantity"></td> </tr> <tr> <td colspan="3"><button type="submit" name="btnsave" class="btn btn-default"> <span class="glyphicon glyphicon-save"></span> Save </button> </td> </tr> </table> <input type="button" class="add-row" value="Add Row"> </form> <div id="markup_model" class="hide"> <table> <tr> <td> <select name="nameitem" class="country"> <option>Select Item</option> <?php // $stmt=$user_home->runQuery("SELECT * FROM item ORDER BY Sr ASC "); // $stmt->execute(); // // if( $stmt->rowCount() > 0 ){ // while( $row=$stmt->fetch( PDO::FETCH_ASSOC ) ){ // echo "<option value='{$row['IRN']}'>{$row['Name']}</option>"; // } // } ?> <option value='ZZ2017TF11A1'>Shirt</option> <option value='ZZ2017TF11A2'>Pant</option> </select> </td> <td> <select class="country" name="namewhat"></select> </td> <td> <input type="text" name="nameqty" value="" placeholder="Quantity" /> </td> </tr> </table> </div> </div> <script src="js/jquery-2.1.4.min.js"></script> <script type="text/javascript"> $(document).ready(function() { var cont = 3; var qty = "qty"; var item = "item"; var what = "service"; $(".add-row").click(function() { var nameqty = qty + cont; var nameitem = item + cont; var namewhat = what + cont; var markup = $('#markup_model tbody'); $(markup).find('.country:eq(0)').attr('name', nameitem); $(markup).find('.country:eq(1)').attr('name', namewhat); $(markup).find('input').attr('name', nameqty); $(markup.html()).insertBefore($('button[type="submit"]').closest("tr")); cont++; }); $('.container').on( 'change', '.country', function () { //console.log($(this).attr('name')); var oSelItem = $(this).attr('name'); var lastChar = oSelItem.substr(oSelItem.length -1); var prevChar = oSelItem.substr(0, oSelItem.length-1); //console.log(prevChar); if(prevChar === "item") { var oSelService = "service" + lastChar; console.log(oSelItem + " " + oSelService); console.log($(this).val()); var thisId = $(this).val(); $.ajax({ type: 'post', url: 'ajax.php', data: { 'id':thisId, 'action':'get_dependent_menu' }, success: function (response) { //alert(response); console.log(oSelService); $('select[name="'+ oSelService +'"]').html(response); } }); } }); }); </script> </body> </html>
And ajax.php
<?php echo "<option value='Select Service'>Select Service (Pant)</option>"; echo "<option value='12121211'>Iron</option>"; ?>