jQuery获取select选中的值:val()、text()与那些坑
select取值这事儿,本来是个一分钟就能搞定的小功能。但每次写的时候还是会卡一下:到底该用 .val() 还是 .text()?单选和多选不一样吗?为什么动态插入的 option 取不到值?
这篇就把 select 取值这件事彻底捋一遍,把容易踩的坑都列上。
一、最基本的取值方式
1. 取 value 值
$("#city").val()
这是最常用的一句。假设:
<select id="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
<option value="gz">广州</option>
</select>
那么 $("'#city'").val() 返回的就是 "bj"、"sh" 这样的字符串。
2. 取显示文本
$("#city").text()
返回的是当前选中项的文本,比如 "北京"。
3. 取 option 元素本身
$("#city option:selected")
拿到的是一个 jQuery 对象,里面装着当前选中的那个 option 元素。想拿它的各种属性就用它:
$("#city option:selected").attr("value") // value 属性
$("#city option:selected").text() // 文本
$("#city option:selected").index() // 索引,从 0 开始
$("#city option:selected").data("id") // 自定义 data-* 属性
二、单选还是多选,val() 返回什么?
这是最容易搞混的地方。
单选 select:val() 返回一个字符串,比如 "bj"。
多选 select(加了 multiple 属性):val() 返回一个数组,包含所有选中项的 value。
<select id="hobby" multiple>
<option value="read">阅读</option>
<option value="game">游戏</option>
<option value="run">跑步</option>
</select>
// 假设选中了"阅读"和"跑步"
$("#hobby").val() // ["read", "run"]
所以处理多选的时候要当数组用:
var vals = $("#hobby").val() || [];
vals.forEach(function (v) {
console.log(v);
});
那能不能用 .text() 取多选的文本?不行。.text() 只返回第一个选中项的文本,多选场景下拿它会漏数据。多选请用这个:
$("#hobby option:selected").map(function () {
return $(this).text();
}).get()
// ["阅读", "跑步"]
三、value 是空字符串的情况
很多人在 select 开头放一个"请选择":
<select id="city">
<option value="">请选择城市</option>
<option value="bj">北京</option>
</select>
用户没选的时候,$("#city").val() 返回的是 ""(空字符串),不是 null,也不是 undefined。很多人写 if (!val) 判断其实是对的,但写成 if (!val.length) 就出问题了——空字符串有 length,值是 0。
更稳妥的判断方式:
var v = $("#city").val();
if (v === "" || v === null) {
// 用户没选择
}
或者直接判断选项索引:
if ($("#city")[0].selectedIndex === 0) {
// 还停在第一项,通常就是"请选择"
}
四、动态插入 option 后取不到值
这个坑很多人在项目里遇到。一开始 select 是空的,通过 ajax 拿到数据后再动态添加 option,结果 .val() 拿到的还是空。
原因有两个:
第一个是添加 option 的时候没给 value 属性。比如:
// 错误写法:只设置了 text,没设 value
$("#city").append($("<option>" + data.name + "</option>"));
这种情况下 option 的 value 默认等于它的文本。如果中文里带空格或者符号,取值就很容易出问题。
正确写法是显式设置 value:
$("#city").append(
$("<option></option>")
.val(data.id)
.text(data.name)
);
第二个原因是 select 本身 disabled,或者有 option 是 disabled 的。disabled 的选项无法被选中,取不到值属于正常行为。
五、联动下拉框的标准写法
省市联动是最经典的两级联动场景,写法上有个细节容易出问题。
$("#province").on("change", function () {
var pid = $(this).val();
if (!pid) {
$("#city").empty();
return;
}
$.ajax({
url: "/api/cities",
data: { pid: pid },
dataType: "json",
success: function (res) {
var $city = $("#city").empty();
res.data.forEach(function (item) {
$city.append(
$("<option></option>")
.val(item.id)
.text(item.name)
);
});
}
});
});
注意 change 事件里必须先 .empty() 清空旧数据,否则切换省份后旧城市还留在下拉里,用户会选到不属于当前省份的城市。
另外,在 .change 回调里用 $(this) 拿到的是被选中的 select 元素;如果写成 $("#province") 也没问题,但 $(this) 更快,也不容易在嵌套的回调里搞混上下文。
六、设置选中状态
取值的反向操作:让某个选项变成选中状态。
// 单选
$("#city").val("sh");
// 多选
$("#hobby").val(["read", "run"]);
这里有个坑:.val("sh") 生效的前提是页面上确实存在 value="sh" 的 option。如果数据是异步加载的,select 还是空的,这时设置选中是无效的,得等 option 插入之后再设置。
插入完成后再设置:
$.get("/api/cities", function (res) {
var $city = $("#city").empty();
res.data.forEach(function (item) {
$city.append($("<option></option>").val(item.id).text(item.name));
});
$city.val(targetId); // 此时才有效
});
用 .attr("selected", "selected") 也能选中,但那是 jQuery 1.9 之前的写法,新版本推荐用 .val(),或者需要同时操作 DOM 属性时用 .prop("selected", true)。
七、表单提交时的取值
如果你要提交到后端,name 属性别忘了写:
<select name="city" id="city">
<option value="bj">北京</option>
<option value="sh">上海</option>
</select>
// 方式一:表单序列化,最省事
$.post("/api/order", $("#myForm").serialize());
// 方式二:手动取
var city = $("#city").val();
serialize() 的结果里,如果某个 select 没有选中任何项,它的值不会出现在参数字符串里,接收方拿不到这个 key。如果后端需要区分"没选"和"选了空值",建议手动取值传给后端。
八、原生 JS 的写法
jQuery 之外,浏览器原生 API 其实也不复杂:
var el = document.getElementById("city");
var value = el.value; // value
var text = el.options[el.selectedIndex].text; // 文本
多选的话用 selectedOptions:
var arr = Array.from(el.selectedOptions).map(function (opt) {
return opt.value;
});
现在很多新项目已经不引 jQuery 了,如果只是取值,原生写法更轻。jQuery 的价值主要在于简化 DOM 操作、事件处理和 AJAX,单纯取值用不上它。
九、几个容易混的点
- .val() 和 .text() 对 select 的行为:.val() 读 value 属性,.text() 读 option 里的文本内容。对 input 来说 .val() 读的是 value 属性,.text() 读的是显示文本,两个不一样;但对 select 来说,.text() 读的是选中 option 的文本,这个"巧合"容易让人搞混。
- option 没有 value 属性时:value 等于 text 内容。所以 $("<option>北京</option>") 的 val() 就是 "北京"。
- 复选框组:$(":checked") 拿到的是所有被选中的复选框集合,取值要循环。
- jQuery 版本差异:1.9 之后移除了 .live(),.size() 改成了 .length,.attr() 和 .prop() 的职责划分更明确(布尔属性用 .prop())。
十、总结
记住三句话基本够用了:
单选取值用 $("'#id'").val(),多选取值用 val() 拿到数组、option:selected 拿文本;动态插入 option 时必须显式设置 .val(),否则取不到值;取值前先想想是不是空字符串、是不是多选、是不是还没加载完数据。
剩下的就是记住 .val() 取值、.text() 取文本、option:selected 取元素这三个的分工,基本不会出错了。