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

Chrome/Firefox中while+startsWith触发调用栈溢出问题

嘿,这个问题我太熟了!咱们来一步步拆解并解决它:

问题根源分析

你遇到的「栈溢出」错误主要来自两个关键问题,Chrome和Firefox的调用栈限制更严格,所以最先暴露出来,Safari只是暂时没触发而已:

  1. 重复绑定事件处理器:每次custom_event_one、custom_event_two或者页面ready时,你都会调用select_parent_instead_of_current(),而这个函数每次都会给select元素新增一个change事件处理器——事件绑定是会叠加的!次数多了,触发一次change就会执行N遍逻辑,直接把调用栈撑爆。
  2. 递归触发change事件:当你选中父选项时,手动调用了.change(),这会再次触发change事件处理器,形成无限递归循环,很快就会达到浏览器的调用栈上限。
修复后的代码方案

我给你重构了代码,从根源解决这两个问题:

<select name="my-select" class="my-class">
    <option value="0">All</option>
    <option value="value-1">Value 1</option>
    <option value="value-1-child">- Value 1 child</option>
    <option value="value-2">Value 2</option>
    <option value="value-2-child-1">- Value 2 child 1</option>
    <option value="value-2-child-2">- Value 2 child 2</option>
    <option value="value-3">Value 3</option>
</select>
/**
 * Function to find previous option in select that does NOT have a "-" prefix.
 * If current option HAS such prefix, it scans previous options until one WITHOUT it is found (the parent) and selects that one.
 * If current option HAS NO such prefix, it can skip.
 */
function select_parent_instead_of_current() {
  // 用命名空间事件,移除旧处理器再绑定新的,避免重复叠加
  jQuery('select[name="my-select"]').off('change.parentSelect').on('change.parentSelect', function(){
    const $select = jQuery(this);
    const $selectedOption = $select.find('option:selected');
    const selectedText = jQuery.trim($selectedOption.text());

    if (selectedText.startsWith('-')) {
      let $prevOption = $selectedOption.prev();

      // 遍历前序选项,同时加长度判断防止越界
      while ($prevOption.length > 0 && jQuery.trim($prevOption.text()).startsWith('-')) {
        $prevOption = $prevOption.prev();
      }

      // 确保找到有效选项,且不是当前已选中的(避免无意义操作)
      if ($prevOption.length > 0 && !$prevOption.is(':selected')) {
        // 直接通过value设置选中状态,不触发change事件,杜绝递归
        $select.val($prevOption.val());
        
        // 如果你需要触发其他依赖change事件的逻辑,可以用下面的方式(带标记跳过当前逻辑)
        // $select.trigger('change', {skipParentCheck: true});
      }
    }
  });
}

/**
 * Fire the function on ready, and other events I require
 */
jQuery( document ).on( 'custom_event_one custom_event_two ready', function( event, data ) {
    select_parent_instead_of_current();
});
关键修改点说明
  • 避免重复绑定:用off('change.parentSelect') + on('change.parentSelect')的命名空间事件写法,每次绑定前先移除旧的处理器,不会出现多处理器叠加的问题。
  • 杜绝递归触发:改用$select.val($prevOption.val())设置选中状态,这个方法不会触发change事件,从根源切断了递归循环。
  • 优化DOM查询:缓存$select、$selectedOption等jQuery对象,避免重复查询DOM,提升性能。
  • 增加边界判断:遍历前序选项时加$prevOption.length > 0,防止遍历到最顶部无选项时出现报错。
  • 精准选中选项:原代码用option:contains()存在匹配歧义(比如"Value 2"和"Value 22"会同时匹配),改用value值设置选中,精准度更高。

如果你的业务逻辑确实需要在选中父选项后触发其他依赖change的操作,可以给trigger('change')传递一个标记参数,在事件处理器开头判断并跳过当前的父选项选择逻辑,比如:

jQuery('select[name="my-select"]').off('change.parentSelect').on('change.parentSelect', function(event, params){
  // 如果是手动触发的带标记事件,跳过当前逻辑
  if (params?.skipParentCheck) return;
  // 剩下的原有逻辑...
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 18:21:00