You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用PHP和JavaScript实现Country->State->City->按钮的有序追加

问题分析

你遇到的追加内容顺序随机问题,核心原因是三个并行的AJAX请求响应返回时间不固定。在add_function()里,你同时发起了获取国家、州、城市数据的三个异步请求,它们的响应返回顺序不受控制,谁先回来就会先把对应的HTML插入页面,最终导致Country->State->City的顺序被打乱。

另外还有个小bug:你用document.createElement('unique')创建元素是错误的,unique是容器的id,不是合法的HTML标签名,应该创建div元素。

解决方案

我们可以通过Promise封装AJAX请求,用async/await确保请求按顺序执行并渲染,保证Country、State、City的输出顺序严格符合预期。下面是修复后的完整代码:

<div id="unique"> 
  <div class="form-group"> 
    <label class="col-sm-2 control-label">Country Name</label> 
    <div class="col-md-6"> 
      <select name="cname" class="form-control" id="coid<?php echo $c;?>" fd="0" onChange="countryfunction(this);" required> 
        <option >choose country</option> 
        <?php 
        include('include/db.php'); 
        $sql="select id,country_name from country"; 
        $query=mysqli_query($con,$sql) or die("error"); 
        while($row=mysqli_fetch_array($query,MYSQLI_BOTH)) { ?> 
          <option value="<?php echo $country_id=$row['id'];?>"><?php echo $row['country_name']; ?></option> 
        <?php } ?>
      </select> 
      <span class="help-block"></span> 
    </div> 
  </div> 
  <div class="form-group"> 
    <label class="col-sm-2 control-label">State Name</label> 
    <div class="col-md-6"> 
      <select name="state" class="form-control" id="sid<?php echo $c;?>" md="0" onChange="statefunction(this);" > 
        <option >Select state</option> 
      </select> 
      <span class="help-block"></span> 
    </div> 
  </div> 
  <div class="form-group"> 
    <label class="col-sm-2 control-label">City Name</label> 
    <div class="col-md-6"> 
      <select name="city" class="form-control" id="cid<?php echo $c;?>" > 
        <option >Select City</option> 
      </select> 
      <span class="help-block"></span> 
    </div> 
  </div> 
  <div class="form-group"> 
    <label class="col-sm-2 control-label"> </label> 
    <div class="col-md-6"> 
      <input type="button" value="+" onClick="add_function()" id="btnid<?php echo $i;?>" /> 
      <span class="help-block"></span> 
    </div> 
  </div> 
</div> 

<script>
// 封装AJAX为Promise,方便用async/await控制顺序
function ajaxPost(url, data) {
  return new Promise((resolve, reject) => {
    const xmlhttp = new XMLHttpRequest();
    xmlhttp.onreadystatechange = function() {
      if (this.readyState === 4) {
        if (this.status === 200) {
          resolve(JSON.parse(this.responseText));
        } else {
          reject(new Error('请求加载失败'));
        }
      }
    };
    xmlhttp.open("POST", url, true);
    xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp.send(data);
  });
}

function countryfunction(see) { 
  const nnn = $(see).attr('fd'); 
  const DATA = document.getElementById("coid"+nnn).value; 
  const sid = document.getElementById("sid"+nnn); 
  const xhttp = new XMLHttpRequest(); 
  xhttp.onreadystatechange = function(){ 
    if (this.readyState === 4 && this.status === 200) { 
      sid.innerHTML = this.responseText; 
    } 
  }; 
  xhttp.open("GET", "stateid1.php?r="+DATA, true); 
  xhttp.send(); 
} 

function statefunction(dee) { 
  const mmm = $(dee).attr('md'); 
  const data = document.getElementById("sid"+mmm).value; 
  const Y = document.getElementById("cid"+mmm); 
  const xhttp = new XMLHttpRequest(); 
  xhttp.onreadystatechange = function(){ 
    if (this.readyState === 4 && this.status === 200) { 
      Y.innerHTML = this.responseText; 
    } 
  }; 
  xhttp.open("GET", "cityidr.php?r="+data, true); 
  xhttp.send(); 
} 

let i = 0; 
let c = 0; 

async function add_function() { 
  // 修复元素创建错误:创建div而非不存在的unique标签
  const objTo = document.getElementById('unique');
  const divtest = document.createElement('div'); 
  i++; 
  c++; 

  try {
    // 1. 先获取并渲染国家下拉框
    const countries = await ajaxPost("demo6.php", "x=" + JSON.stringify({}));
    let countryHtml = "<div class='form-group' > <label class='col-sm-2 control-label'>Country Name</label><div class='col-md-6'><select name='country[]' onChange='countryfunction(this);' fd='"+c+"' id='coid"+c+"' class='form-control'><option>--Choose Country--</option>";
    for (const key in countries) {
      countryHtml += `<option value="${countries[key].id}">${countries[key].country_name}</option>`;
    }
    countryHtml += "</select><span class='help-block'></span></div></div>";
    divtest.innerHTML += countryHtml;

    // 2. 再获取并渲染州下拉框
    const states = await ajaxPost("demo9.php", "y=" + JSON.stringify({}));
    let stateHtml = "<div class='form-group' ><label class='col-sm-2 control-label'>State Name</label><div class='col-md-6'><select name='state[]' onChange='statefunction(this);' md='"+c+"' id='sid"+c+"' class='form-control'><option>--Select state--</option>";
    for (const key in states) {
      stateHtml += `<option value="${states[key].id}">${states[key].state_name}</option>`;
    }
    stateHtml += "</select><span class='help-block'></span></div></div>";
    divtest.innerHTML += stateHtml;

    // 3. 最后获取并渲染城市下拉框
    const cities = await ajaxPost("demo11.php", "z=" + JSON.stringify({}));
    let cityHtml = "<div class='form-group'><label class='col-sm-2 control-label'>City Name</label><div class='col-md-6'><select name='city[]' id='cid"+c+"' class='form-control'><option>--Select City--</option>";
    for (const key in cities) {
      cityHtml += `<option value="${cities[key].id}">${cities[key].city_name}</option>`;
    }
    cityHtml += "</select><span class='help-block'></span></div></div>";
    divtest.innerHTML += cityHtml;

    // 4. 渲染+按钮
    const btnHtml = `<div class='form-group'><label class='col-sm-2 control-label'></label><div class='col-md-6'> <input type='button' value='+' onClick='add_function()' id='btnid${i}' /><span class='help-block'></span></div></div>`;
    divtest.innerHTML += btnHtml;

    // 将组装好的整行内容添加到容器
    objTo.appendChild(divtest);
  } catch (error) {
    console.error('加载数据出错:', error);
  }
}
</script>
关键改进点
  1. Promise+async/await控制异步顺序:把AJAX请求封装成Promise,用await确保前一个请求完成并渲染后,再执行下一个请求,严格保证Country->State->City的顺序。
  2. 修复元素创建错误:将document.createElement('unique')改为document.createElement('div'),避免创建无效标签。
  3. 优化HTML组装逻辑:每个模块的HTML一次性组装完成后再插入,减少innerHTML的修改次数,避免潜在的渲染问题。

额外优化建议:如果后端支持,可以把三个请求合并成一个接口,一次性返回国家、州、城市的初始数据,减少HTTP请求数量,提升页面加载效率。

内容的提问来源于stack exchange,提问作者shivam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:36:20