首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 綜合知識

PHP 實現二級聯動下拉選單:從 iframe 方案到 AJAX 現代化重構

     編輯:PHP 綜合知識

二級聯動下拉選單(如「省份 → 城市」、「分類 → 子分類」)是常見 UI 需求。早期常用 iframe 方案,現代開發應採用 AJAX + JSON。本文對比兩種實作,提供完整前後端程式碼與遷移指南。

方案一:傳統 iframe 方案 (原始碼重構)

index.html

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>聯動下拉 - iframe 版</title></head><body>
<form name="myfrm">
  <select name="mlist" onchange="loadCities()">
    <option value="">請選擇省份</option>
    <option value="北京">北京</option>
    <option value="通化">通化</option>
  </select>
  <select name="slist" id="slist" disabled><option>請先選省份</option></select>
</form>
<iframe id="frame" src="about:blank" style="display:none;"></iframe>
<script>
function loadCities() {
    const province = document.myfrm.mlist.value;
    const frame = document.getElementById('frame');
    frame.src = 'list.php?city=' + encodeURIComponent(province);
}
function updateCities(optionsHtml) {
    const slist = document.myfrm.slist;
    slist.innerHTML = optionsHtml;
    slist.disabled = false;
}
</script>
</body></html>

list.php

<?php
$data = [
    '北京' => ['東城區','西城區','朝陽區','海淀區'],
    '通化' => ['東昌區','二道江區','通化縣','輝南縣'],
];
$city = $_GET['city'] ?? '';
$cities = $data[$city] ?? [];

$options = '<option value="">請選擇城市</option>';
foreach ($cities as $c) {
    $options .= "<option value=\"$c\">$c</option>";
}

// 回傳 JS 片段更新父頁面
echo "<script>parent.updateCities(" . json_encode($options) . ");</script>";

iframe 方案缺點

  • 阻塞主頁面載入指示器
  • 跨域受限 (同源策略)
  • 無法直接處理錯誤狀態 (HTTP 500 等)
  • SEO 不友善、無法書籤狀態
  • 維護困難:邏輯分散在 PHP 字串拼接中

方案二:現代 AJAX + JSON 方案 (推薦)

index.html (Vue 3 / 原生 JS 均適用)

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>聯動下拉 - AJAX 版</title></head><body>
<div id="app">
  <select v-model="province" @change="loadCities">
    <option value="">請選擇省份</option>
    <option v-for="p in provinces" :key="p" :value="p">{{ p }}</option>
  </select>
  <select v-model="city" :disabled="!cities.length">
    <option value="">請選擇城市</option>
    <option v-for="c in cities" :key="c" :value="c">{{ c }}</option>
  </select>
</div>
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<script>
const { createApp, ref, onMounted } = Vue;
createApp({
  setup() {
    const province = ref('');
    const city = ref('');
    const cities = ref([]);
    const provinces = ['北京', '通化']; // 或從 API 獲取

    async function loadCities() {
      if (!province.value) { cities.value = []; return; }
      try {
        const res = await fetch('api/cities.php?province=' + encodeURIComponent(province.value));
        if (!res.ok) throw new Error('Network error');
        cities.value = await res.json();
      } catch (e) {
        console.error(e); cities.value = [];
      }
    }
    return { province, city, cities, provinces, loadCities };
  }
}).mount('#app');
</script>
</body></html>

api/cities.php

<?php
header('Content-Type: application/json; charset=utf-8');

$data = [
    '北京' => ['東城區','西城區','朝陽區','海淀區'],
    '通化' => ['東昌區','二道江區','通化縣','輝南縣'],
];

$province = $_GET['province'] ?? '';
$cities = $data[$province] ?? [];

echo json_encode($cities, JSON_UNESCAPED_UNICODE);

進階:資料庫驅動版

<?php
// api/cities.php (PDO 版)
require 'db.php'; // 返回 $pdo

$province = $_GET['province'] ?? '';
if (!$province) { echo '[]'; exit; }

$stmt = $pdo->prepare('SELECT name FROM cities WHERE province = ? ORDER BY sort');
$stmt->execute([$province]);
$cities = $stmt->fetchAll(PDO::FETCH_COLUMN);

header('Content-Type: application/json');
echo json_encode($cities, JSON_UNESCAPED_UNICODE);

前端框架整合範例

框架關鍵點
Vue 3v-model 雙向綁定、@change 觸發 fetch、ref 響應式陣列
ReactuseState 管理狀態、useEffect 監聽省份變化發請求、select onChange
Alpine.jsx-data, x-model, @change, fetch 輕量級
原生 JSaddEventListener('change'), fetch, innerHTML 更新

優化策略

  • 快取:前端 Map 快取已載入城市,避免重複請求
  • 防抖:輸入框聯動建議用 debounce(300ms),下拉選單通常不需
  • 載入狀態:顯示 Spinner、禁用城市選單、aria-busy 無障礙
  • 錯誤處理:網路失敗時保留上次結果、顯示 Toast 提示
  • SEO/分享:URL 加入 ?province=北京&city=朝陽區,頁面載入時自動恢復狀態

遷移檢查清單

  1. 建立 api/cities.php 回傳 JSON
  2. 前端移除 iframe、parent.updateCities 全域函數
  3. 改用 fetch / axios 非同步請求
  4. 處理載入中、錯誤、空狀態 UI
  5. 測試跨域 (CORS) 若 API 部署在不同域
  6. 加入單元測試 (Jest/Vitest) 驗證邏輯

現代 AJAX 方案分離關注點、提升使用者體驗、便於測試與維護,是聯動下拉的標準實作方式。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved