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 取元素这三个的分工,基本不会出错了。