
select下拉選項(xiàng)的selectedIndex屬性你用過嗎前陣子在某前端群里看到一個提問明明給下拉框設(shè)置了value頁面也渲染出了對應(yīng)的option為什么提交表單時卻拿不到預(yù)期選項(xiàng)問題最后定位到一行很老實(shí)的代碼——有人用selectedIndex做了初始定位又在另一個事件里改了value兩套邏輯互相覆蓋。評論區(qū)有人說這個API都快被忘記了。我愣了一下仔細(xì)想想確實(shí)這些年框架用多了原生select上還有多少人在意selectedIndex這個屬性它到底是干什么的和value比優(yōu)勢在哪為什么動態(tài)下拉里最常翻車的也是它這篇文章想把這家伙重新翻出來聊透順便把我在幾個項(xiàng)目里踩過的坑、寫過的輪子一起交個底。1. selectedIndex與value到底差了點(diǎn)啥兩個API的實(shí)質(zhì)分工1.1 從DOM結(jié)構(gòu)理解selectedIndex的底層邏輯很多人第一眼看到selectedIndex覺得它不就是“選中了第幾項(xiàng)”嘛讀一下返回個數(shù)字寫一下選中對應(yīng)項(xiàng)。這么說沒錯但它底層的工作方式比這更有意思。select元素在DOM里不是孤立存在的它下面掛著一個options集合也就是HTMLCollection。selectedIndex本質(zhì)上是“當(dāng)前選中項(xiàng)在這個集合里的整數(shù)下標(biāo)”。選中第一項(xiàng)就是0第二項(xiàng)就是1沒有選中任何一項(xiàng)就是-1。這種設(shè)計是瀏覽器最早實(shí)現(xiàn)下拉框交互時就定下來的一切操作圍繞集合的下標(biāo)展開而不是圍繞選項(xiàng)的value展開。這里有個容易混淆的細(xì)節(jié)value屬性也是select的一部分它返回的是“選中項(xiàng)option的value值”。但select本身沒有直接的value存儲它所有狀態(tài)都是通過“哪一項(xiàng)被選中”反映出來的。你讀select.value瀏覽器底層查的還是selectedIndex你寫select.value瀏覽器做的也是遍歷options找到對應(yīng)value的那個option然后把它選中。所以在寫代碼時value是“語義化”的selectedIndex是“結(jié)構(gòu)性”的。1.2 為什么說selectedIndex在有些場景更可靠既然value這么語義化為什么還要用selectedIndex我的體會是value在兩種情況下容易“失真”。第一種是選項(xiàng)的value有重復(fù)。HTML規(guī)范里其實(shí)沒禁止多個option擁有相同的value值。一旦兩個選項(xiàng)value一樣select.value就分不清你到底選中了哪個。這時候只有selectedIndex能精確表達(dá)位置。第二種是選項(xiàng)根本沒有value。比如你直接寫了option北京/option沒給value屬性。這種情況option.value返回的是文本內(nèi)容即“北京”??粗鴽]問題但如果文本里有特殊字符、空格、或者后續(xù)顯示文案變了value跟著變后端存儲就可能受污染。而selectedIndex是不關(guān)心文本的它只管位置位置穩(wěn)定它就穩(wěn)定。我在做模擬項(xiàng)目X的表單頁時遇到過一個需求用戶在下拉里選學(xué)歷選項(xiàng)有“初中”“高中”“大?!薄氨究啤薄按T士”“博士”。產(chǎn)品要求只允許選“大?!奔耙陨系陀凇按髮!睍r彈提示并回退到默認(rèn)“大?!?。如果我用value判斷還要維護(hù)一個等級對照表用selectedIndex直接比較下標(biāo)就一行if。這種“位置即業(yè)務(wù)等級”的場景selectedIndex反而比value更直接。1.3 讀寫規(guī)則和瀏覽器差異讀selectedIndex很簡單直接拿屬性值。寫的規(guī)則也直接賦值整數(shù)即可。這里要注意幾點(diǎn)賦一個超過options長度的值瀏覽器靜默忽略不會報錯選中態(tài)保持原樣。賦負(fù)數(shù)在絕大多數(shù)瀏覽器里會被當(dāng)成取消選擇selectedIndex變成-1。但舊版本瀏覽器表現(xiàn)不一有的會把-1當(dāng)成不合法值直接忽略。若select有multiple屬性selectedIndex只返回第一個被選中的索引不能列舉全部。多選場景要用selectedOptions或遍歷options逐個看selected。這些邊界問題平時不碰沒啥一旦碰上就會感受到“結(jié)構(gòu)API”的擰巴。我自己的習(xí)慣是單選下拉優(yōu)先用selectedIndex處理邏輯需要拿給后端的數(shù)據(jù)時再從選中項(xiàng)身上取value。分開用反而兩邊都清爽。2. 讀selectedIndex的高頻業(yè)務(wù)場景聯(lián)動、向?qū)?、列表選中態(tài)2.1 聯(lián)動下拉為什么用索引比value省心聯(lián)動下拉是selectedIndex出現(xiàn)最多的場景。比如選省份聯(lián)動城市選一級分類聯(lián)動二級分類。傳統(tǒng)寫法是監(jiān)聽change事件拿到當(dāng)前選中的省份再去請求或從本地數(shù)據(jù)里篩城市。用value寫的話代碼長這樣const provinceSelect document.querySelector(#province); const citySelect document.querySelector(#city); provinceSelect.addEventListener(change, () { const provinceValue provinceSelect.value; citySelect.innerHTML ; const cities cityMap[provinceValue] || []; cities.forEach(city { const opt document.createElement(option); opt.value city.code; opt.textContent city.name; citySelect.appendChild(opt); }); });用selectedIndex寫則可以完全不依賴value里的業(yè)務(wù)編碼provinceSelect.addEventListener(change, () { const idx provinceSelect.selectedIndex; citySelect.innerHTML ; const cities cityList[idx] || []; // 后續(xù)渲染邏輯一致 });兩種方式都能跑。差別在哪在數(shù)據(jù)結(jié)構(gòu)的穩(wěn)定性上。如果后端接口給的城市數(shù)據(jù)是按省份數(shù)組的索引對齊的用value反而要做一次indexOf查找。更關(guān)鍵的是當(dāng)value的編碼規(guī)則調(diào)整比如從數(shù)字編碼改成省份拼音如果你只在展示層用value去當(dāng)下標(biāo)后續(xù)維護(hù)成本一下就上來了。直接用selectedIndex對應(yīng)數(shù)組下標(biāo)結(jié)構(gòu)和數(shù)據(jù)源對齊改數(shù)據(jù)時幾乎不用動邏輯。2.2 步驟向?qū)Ш捅韱畏植狡饔涀∮脩糇叩侥囊徊阶鲞^多步表單的人應(yīng)該都碰過“當(dāng)前步驟指示器”。有些產(chǎn)品用下拉框來切換步驟尤其是批量配置場景選第幾步就展示對應(yīng)步驟的區(qū)塊。這種地方用selectedIndex記錄步驟位置再合適不過。我做過一個某跨平臺系統(tǒng)的配置向?qū)ё髠?cè)是一個select右側(cè)是步驟面板。用戶切到第3步右側(cè)顯示第3步內(nèi)容還要記住他是否訪問過第4步。這時候我用selectedIndex作為“步驟指針”配合一個數(shù)組去記錄每個步驟的完成狀態(tài)const stepSelect document.querySelector(#stepSelector); const stepStatus [false, false, false, false, false]; stepSelect.addEventListener(change, () { const current stepSelect.selectedIndex; stepStatus[current] true; renderStepPanel(current); updateVisitedFlag(stepStatus); });如果用value來記錄就得定義step-1、step-2這類約定還得解析字符串。selectedIndex天然就是數(shù)字跟數(shù)組下標(biāo)、步驟編號天然同構(gòu)少掉了整個parse環(huán)節(jié)。這個場景里它就是最順手的那把刀。2.3 表格行選中與下拉選項(xiàng)的同步還有一個我近期才摸索出來的妙用下拉列表和表格行選中做雙向同步。比如頁面上有一個表格每行數(shù)據(jù)對應(yīng)下拉里的一個選項(xiàng)用戶既可以直接在下拉里選也可以點(diǎn)擊表格行切狀態(tài)。下拉被程序化切換到某行時我需要高亮對應(yīng)表格行。這時selectedIndex的價值在于它本身就是“行號”。我只需要tableRowClickHandler(rowIndex) { mySelect.selectedIndex rowIndex; highlightTableRow(rowIndex); } mySelect.addEventListener(change, () { const row mySelect.selectedIndex; highlightTableRow(row); });不需要維護(hù)“選中的業(yè)務(wù)主鍵 - 行號”的映射關(guān)系。第一版我用的是valueid映射后來發(fā)覺純屬多余因?yàn)橄吕樞蚝捅砀耥樞蚴峭粋€數(shù)據(jù)源渲染出來的索引天然一一對應(yīng)。這個場景讓我對selectedIndex徹底改觀它不是老掉牙的舊API而是恰到好處的輕量工具。3. 動態(tài)增刪option后selectedIndex為什么最容易翻車3.1 一次實(shí)打?qū)嵉牟瓤酉雀膐ptions再賦值下標(biāo)對不上了有一回做一個多級篩選器邏輯是第一個下拉選分類第二個下拉的選項(xiàng)根據(jù)分類動態(tài)生成。為了用戶體驗(yàn)我保留了用戶之前選中過的二級選項(xiàng)切換一級分類時先備份當(dāng)前二級索引重新生成選項(xiàng)后嘗試恢復(fù)。代碼如下const backupIndex secondSelect.selectedIndex; buildOptions(secondSelect, newList); secondSelect.selectedIndex backupIndex;看上去沒問題但實(shí)測中發(fā)現(xiàn)一個詭異現(xiàn)象有時候恢復(fù)的選項(xiàng)和用戶之前看到的不是同一個。排查半天發(fā)現(xiàn)問題不在恢復(fù)那一步而在備份那一步——我在重新生成選項(xiàng)之前備份的是舊列表的索引可舊列表和新列表的內(nèi)容排序完全不一樣。比如舊列表第3項(xiàng)是“廣東”新列表按拼音重排后第3項(xiàng)變成了“北京”。索引位置一樣但含義完全不同。這就是典型的下標(biāo)體系被打破的案例。selectedIndex是位置敏感的一旦options數(shù)組結(jié)構(gòu)變化舊索引就失效了。解決方案不是硬恢復(fù)索引而是備份業(yè)務(wù)主鍵重建后先按主鍵找新位置再賦值const backupValue secondSelect.value; buildOptions(secondSelect, newList); restoreSelectByValue(secondSelect, backupValue);專門寫了一個restore函數(shù)里面遍歷options找匹配value。這函數(shù)前面說過value失效場景很少所以在動態(tài)列表里value更可靠。這件事之后我的紅線是靜態(tài)列表用selectedIndex順手動態(tài)列表優(yōu)先用value。3.2 HTMLCollection實(shí)時刷新帶來的“隱式位移”另一個更隱蔽的坑options是個實(shí)時集合你對它做增刪操作索引會立刻重新編號。比如當(dāng)前選中第5項(xiàng)下標(biāo)4此時刪掉第1項(xiàng)你選中的項(xiàng)自動變成第4項(xiàng)下標(biāo)3selectedIndex跟著變。這個行為本身符合直覺但如果你沒有意識到“索引是實(shí)時變化的”就容易寫錯邏輯。比如const delIndex select.selectedIndex; select.remove(delIndex); // 此時如果刪的是前面項(xiàng)原選中項(xiàng)下標(biāo)已經(jīng)變了如果在刪除后立刻用舊的delIndex去處理原選中項(xiàng)拿到的一定是另一項(xiàng)或undefined。我的做法是凡是涉及刪除后要拿到原選中項(xiàng)先保存元素引用const selectedOption select.options[select.selectedIndex]; select.remove(select.selectedIndex); // 后續(xù)處理用selectedOption而不是索引類似的問題也會出現(xiàn)在appendChild插入選項(xiàng)時。插入位置靠前后面所有選項(xiàng)索引都往后挪此時selectedIndex如果沒重設(shè)雖然指向的option沒變因?yàn)镈OM引用沒變但與用戶視覺上的“當(dāng)前選中順序”就出現(xiàn)了不一致。交互層觸發(fā)的事件和選中態(tài)的對齊需要手動處理。3.3 空列表和重置時機(jī)的配合問題動態(tài)下拉還有個常見要求當(dāng)上級選項(xiàng)切換后下級列表清空并處于“請選擇”狀態(tài)。清空操作一般是innerHTML此時selectedIndex自動變成-1。這操作沒問題問題在某些人喜歡在后面手動補(bǔ)一個“請選擇”選項(xiàng)然后選中它select.innerHTML option value請選擇/option; select.selectedIndex 0;這么寫沒問題。但如果是先appendChild加了一個“請選擇”再立刻調(diào)用select.value嘗試選中它在某些瀏覽器里會因?yàn)関alue是空字符串且option的value屬性也是空字符串而命中但在另一些瀏覽器里select.value賦值空字符串可能不觸發(fā)選中邏輯。我后來統(tǒng)一用selectedIndex0來完成這個“重置到占位項(xiàng)”的動作完全不依賴value跨瀏覽器穩(wěn)定得多。這類問題不細(xì)測很難發(fā)現(xiàn)尤其當(dāng)你在一個復(fù)雜的表單里同時用了多種重置方式出bug時根本不知道該懷疑誰。建議團(tuán)隊(duì)里約定一種固定的重置寫法少讓成員自由發(fā)揮。4. selectedIndex的邊界情況和多選陷阱4.1 selectedIndex -1是真空態(tài)還是假空態(tài)selectedIndex為-1表示沒有任何選項(xiàng)被選中。這個值在業(yè)務(wù)里常用于表達(dá)“未選擇”狀態(tài)比如沒填完的表單項(xiàng)校驗(yàn)時判斷selectedIndex是否等于-1就知道用戶有沒有選過。但這里有一個讓人迷惑的點(diǎn)明明我設(shè)置了selectedIndex-1頁面上卻還能看到第一項(xiàng)是“選中”的高亮樣式。原因在于大部分瀏覽器的默認(rèn)行為如果select沒有選項(xiàng)或沒有選中項(xiàng)且沒有寫required瀏覽器在用戶不操作時會把第一個可選項(xiàng)當(dāng)成默認(rèn)顯示項(xiàng)。這是瀏覽器“視覺回退”不是DOM狀態(tài)真的改了。我在某表單驗(yàn)證邏輯里就踩過下拉框是允許不選的我設(shè)置了selectedIndex-1然后校驗(yàn)通過結(jié)果用戶沒做任何操作提交時select.value取出來的卻是第一項(xiàng)的value后端也收到了導(dǎo)致一條不完整的記錄入庫。當(dāng)時的修復(fù)很簡單校驗(yàn)時不只看selectedIndex還要看用戶有沒有真實(shí)交互過let userInteracted false; select.addEventListener(change, () { userInteracted true; }); // 提交時 if (!userInteracted || select.selectedIndex -1) { // 當(dāng)作未選擇 }很多同學(xué)喜歡用“視覺上是否默認(rèn)選中第一項(xiàng)”來判斷有沒有選這個思路在原生select里是危險的。視覺高亮不代表selectedIndex有值。如果必須讓用戶強(qiáng)制選擇建議options里顯式加一個disabled的“請選擇”占位項(xiàng)并且不要給它value。這樣能規(guī)避很多莫名其妙的默認(rèn)選中問題。4.2 disabled選項(xiàng)會讓索引讀起來疑似“跳位”下拉里經(jīng)常有不可選項(xiàng)比如“該地區(qū)已停運(yùn)”。disabled的option依然占據(jù)一個索引位置但用戶永遠(yuǎn)無法選中它。selectedIndex讀出來的位置是“當(dāng)前選中的那個合法項(xiàng)的索引”。如果你以為“第一項(xiàng)就是下標(biāo)0”但第一項(xiàng)其實(shí)是disabled的占位項(xiàng)讀出來還會是0嗎實(shí)測如果第一項(xiàng)disabled且沒選中任何項(xiàng)selectedIndex在大部分瀏覽器里是-1但在用戶已經(jīng)選中第二項(xiàng)之后再通過程序把第一項(xiàng)改為disabledselectedIndex依然是第二項(xiàng)的索引比如1頁面視覺也保持第二項(xiàng)選中。這里不會出現(xiàn)位移只是你要注意渲染綁定索引時不要假設(shè)“options[i]一定可選”做高亮等邏輯前要主動檢查option.disabled。另外有一種業(yè)務(wù)偽需求“用戶選中了A項(xiàng)后B項(xiàng)自動不可選”。很多人會去改option的disabled屬性。這沒問題但是disabled改動不會觸發(fā)change事件用戶已經(jīng)選中的C項(xiàng)也不受影響。如果你希望通過狀態(tài)聯(lián)動自動切換選中項(xiàng)還是得手動改selectedIndex。我在一個聯(lián)動配置里就是先改disabled再判斷當(dāng)前選中項(xiàng)是否變成disabled如果是再selectedIndex往下一個可選項(xiàng)目移動邏輯寫起來比預(yù)想的多幾行但這些細(xì)節(jié)躲不過。4.3 多選select里讀selectedIndex的“誤導(dǎo)性”多選模式下selectedIndex返回的是第一個被選中項(xiàng)的下標(biāo)。聽起來像是個便利功能實(shí)際上最容易被誤用。比如你寫了const first mySelect.selectedIndex;下意識以為拿到了第一個選中項(xiàng)。其實(shí)如果用戶選的順序是第3項(xiàng)、第1項(xiàng)selectedIndex返回的是1因?yàn)橄聵?biāo)小的先被選中而不是3。你要是按“第一個被點(diǎn)選”的語義去理解就錯了。多選場景我建議直接繞開selectedIndex用Array.from(select.selectedOptions)拿所有選中項(xiàng)或者遍歷options過濾selected。這兩個API在多選里語義明確不會產(chǎn)生歧義。selectedIndex這個屬性天生是單選模型的產(chǎn)物放到多選里就只是個兼容性殘留別拿它當(dāng)業(yè)務(wù)邏輯的主干。5. 框架時代selectedIndex還剩下哪些用武之地5.1 React受控組件里為什么基本不用它React的受控select用的是value配合onChange事件。你寫select value{city} onChange{...}React內(nèi)部會遍歷options去匹配value對應(yīng)的項(xiàng)并設(shè)置selected。這時候你如果自己再去動selectedIndex麻煩就來了。受控組件要求“狀態(tài)唯一來源”。value存的是業(yè)務(wù)值selectedIndex只是渲染結(jié)果。如果你在事件里寫了類似selectEl.selectedIndex 1React下次渲染時發(fā)現(xiàn)value還是原來的值會再用value重新設(shè)置選中態(tài)你手動指定的索引就被覆蓋了。這就是標(biāo)題開頭那個群里提問的人遇到的問題——他的value邏輯和selectedIndex邏輯互相打架因?yàn)镽eact的渲染循環(huán)會不斷用value“糾正”DOM。在React里如果需要用索引邏輯正確姿勢是把索引存進(jìn)state再通過value{options[idx]?.value}映射到受控屬性上。這樣索引只是業(yè)務(wù)計算的臨時量最終落到的還是value這個唯一狀態(tài)。硬要在React里用selectedIndex等于和框架的渲染模型對著干。5.2 Vue的v-model和原生selectedIndex的兼容經(jīng)驗(yàn)Vue這邊情況類似v-model對select的處理方式與React大同小異也是基于value做匹配。但在Vue的非受控組件或需要直接操作DOM的指令場景里selectedIndex還有空間。比如我寫過一個自定義指令作用是“讓下拉支持通過程序按索引快速切換”指令內(nèi)部就直接操作selectedIndex不依賴框架狀態(tài)const directive { mounted(el, binding) { el.selectedIndex binding.value; }, updated(el, binding) { if (el.selectedIndex ! binding.value) { el.selectedIndex binding.value; } } };這種指令適用于“下拉項(xiàng)完全由靜態(tài)模板決定、value不承擔(dān)業(yè)務(wù)含義”的場合。比如一個純展示用的序號選擇器選項(xiàng)是“第1頁”“第2頁”這種用value反而要跟著頁面文案變。此時selectedIndex當(dāng)“頁碼指針”用非常順手。但要注意Vue更新DOM是異步的updated鉤子里拿到的el可能還在舊狀態(tài)。實(shí)測經(jīng)驗(yàn)多數(shù)情況下updated鉤子里直接賦值selectedIndex不會出錯但如果選項(xiàng)數(shù)量在數(shù)據(jù)變化時同步增減了索引位置可能和應(yīng)用層期望不一致。穩(wěn)妥做法是在nextTick之后再做索引修正。這個坑不大但碰到一次就夠你記一輩子。5.3 無框架環(huán)境里selectedIndex還是最優(yōu)解之一如果項(xiàng)目還在用原生JS或者你只是在一個獨(dú)立的小組件里實(shí)現(xiàn)一個簡單交互selectedIndex依然是最輕量的API。沒有框架的對比沒有響應(yīng)式的狀態(tài)流轉(zhuǎn)你要做的不過就是“選中第幾個”這件事一行代碼解決。比如我寫過一個批量導(dǎo)入的預(yù)覽組件文件解析后得到的字段順序映射到下拉選項(xiàng)用戶調(diào)換字段時選中某個字段就對應(yīng)數(shù)據(jù)源列號。這個場景value完全無意義selectedIndex直接就是列號fieldSelect.addEventListener(change, () { const colIndex fieldSelect.selectedIndex; previewTable.dataset.columnMap colIndex; });代碼量少得不像話。你非要用value也不是不行但你得維護(hù)columnMap映射表純純給自己加戲。5.4 封裝一個安全的索引操作小工具為了兼顧各種場景我后來把自己常用的一套邏輯封裝成了一個極簡函數(shù)專門處理“按索引選中”的安全操作function safeSetSelectedIndex(selectEl, index, fallbackIndex 0) { if (!selectEl) return -1; if (index 0 index selectEl.options.length) { selectEl.selectedIndex index; return index; } if (fallbackIndex 0 fallbackIndex selectEl.options.length) { selectEl.selectedIndex fallbackIndex; return fallbackIndex; } selectEl.selectedIndex -1; return -1; }它只做一件事檢查范圍賦索引失敗回退。進(jìn)入這個工具函數(shù)前業(yè)務(wù)邏輯先想清楚是備份索引還是備份value。如果是動態(tài)列表我建議先備份value重建后再用value匹配索引最后調(diào)用safeSetSelectedIndex完成定位。這套組合拳寫下來基本覆蓋了我遇到過的全部場景靜態(tài)索引、動態(tài)變化、越界保護(hù)、回退兜底。自己團(tuán)隊(duì)里用它替換了大量繁瑣的if判斷出錯率比之前手動寫低了不少。6. 幾個實(shí)戰(zhàn)命令和代碼片段可以直接抄6.1 輪播式切換下一項(xiàng)有一種需求是點(diǎn)擊按鈕切換到下拉的下一項(xiàng)循環(huán)滾動。用selectedIndex寫起來很順手document.querySelector(#nextBtn).addEventListener(click, () { const select document.querySelector(#demoSelect); const next (select.selectedIndex 1) % select.options.length; select.selectedIndex next; select.dispatchEvent(new Event(change)); });注意最后dispatchEvent那行程序化修改selectedIndex本身不觸發(fā)change事件。如果你后續(xù)邏輯依賴change記得手動派發(fā)。這個細(xì)節(jié)我第一次也漏了導(dǎo)致點(diǎn)擊按鈕后UI變了但聯(lián)動沒反應(yīng)。6.2 根據(jù)選中索引設(shè)置占位提示做表單時經(jīng)常遇到下拉為空需要顯示灰色提示選中后提示消失。selectedIndexCSS類可以這樣配合const select document.querySelector(#reqSelect); function updatePlaceholder() { select.classList.toggle(is-empty, select.selectedIndex -1); } select.addEventListener(change, updatePlaceholder); updatePlaceholder();CSS類單獨(dú)控制顏色和邊框不需要在每次渲染時寫innerHTML。干凈利落。6.3 selectedIndex與鍵盤上移下移的配合原生select在聚焦?fàn)顟B(tài)下按上/下方向鍵就會自動切換選中項(xiàng)而且會觸發(fā)change事件。在某些不支持原生樣式的自定義下拉組件里我們常常要模擬這個行為。簡化版可以這樣寫customSelect.addEventListener(keydown, (e) { if (e.key ! ArrowDown e.key ! ArrowUp) return; e.preventDefault(); const dir e.key ArrowDown ? 1 : -1; const nextIndex Math.min( Math.max(realSelect.selectedIndex dir, 0), realSelect.options.length - 1 ); realSelect.selectedIndex nextIndex; syncCustomUI(); });把真實(shí)但隱藏的select當(dāng)作狀態(tài)容器selectedIndex負(fù)責(zé)“當(dāng)前在哪一項(xiàng)”UI層負(fù)責(zé)畫出來數(shù)據(jù)層從options里取value。這個架構(gòu)在無框架環(huán)境下特別穩(wěn)我做了好幾個自定義下拉都沿用這個思路。7. 我用了這么多年最后留下三條經(jīng)驗(yàn)第一靜態(tài)結(jié)構(gòu)里大膽用selectedIndex動態(tài)結(jié)構(gòu)里三思而后行。選項(xiàng)一旦可能增刪改排序索引就失去了穩(wěn)定性轉(zhuǎn)而以value備份為佳。第二程序改selectedIndex不會觸發(fā)change事件需要手動派發(fā)或顯式調(diào)用業(yè)務(wù)函數(shù)。這個坑在原生環(huán)境極其高頻每次寫完索引賦值先問自己一句后續(xù)邏輯依靠事件嗎依靠就補(bǔ)上dispatch不依靠就確認(rèn)調(diào)用鏈。第三別在受控組件里跟框架搶方向盤。React或Vue都已經(jīng)把value作為狀態(tài)的唯一來源你在外面手動動selectedIndex大概率會被下一次渲染覆蓋。要玩索引就在state層玩映射成value再進(jìn)組件。如果你之前從沒認(rèn)真用過selectedIndex翻翻手頭的舊代碼把它從“曾經(jīng)用過但忘了”的收藏夾里撈出來弄懂它和value的邊界。這種老API不是沒用了它們只是被框架遮住了太久。真正需要的人會在某個深夜的聯(lián)動下拉bug現(xiàn)場跟它重逢然后感嘆一句原來你一直在等我。