根据某些选择javascript禁用下拉列表中的选项

时间:2017-04-27 19:14:31

标签: javascript

因此,当我在相邻的下拉列表中进行某个选择时,我会尝试在一个下拉列表中禁用某些日期。我有20对这些下拉菜单。当它只有1对而不是两者时,我可以让它工作。所以当" month1"被选中,我需要在" day1"无法使用。

data = {
      disabled_days: {
        "June": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 18, 24, 25, 31],
        "July": [1, 2, 3, 4, 8, 9, 15, 16, 22, 23, 29, 30],
        "August": [5, 6, 12, 13, 19, 20, 26, 27]
      }
    }
    //loop through to create this 20 times
    for(x=1;x<=20;x++){
    //get the select month box
    let select_month = document.querySelector('select[name="month'+x+'"]');

    //when month is selected
    select_month.addEventListener('change', function(e){

      //get the name of the month
      let month = select_month[select_month.selectedIndex].textContent;

      //get all the days in the data select box and enable them all
      let day_arr = [].slice.call(document.querySelectorAll('select[name="day'+x+'"] option'))
      for(let day of day_arr) day.disabled = false;

      //loop over all the days to be disabled, find thme in the data ovject based on month and disable those days
      for (let disabled_day of data.disabled_days[month]){
        for (let day of day_arr){
          if (day.textContent == disabled_day) day.disabled = true;
        }
      }
    })
  }

所以我觉得我在串联('select[name="day'+x+'"] option')的某个地方发生了故障,并且月份也一样。 这已经让我搞砸了差不多一个星期,所以任何帮助都会非常感激。

这是关联的php和html:

  <?php for($x=1; $x<=20; $x++){ ?>
              <div class="form-group">
                <label><?php echo "$x"; ?>.</label>
                <select class="form-control" name="month<?php echo $x ;?>">
                  <option selected disabled>- Month -</option>
                  <option value="06">June</option>
                  <option value="07">July</option>
                  <option value="08">August</option>
                </select>
                <select class="form-control" name="day<?php echo $x ;?>">
                  <option selected disabled>- Day -</option>
                  <option value="01">1</option>
                  <option value="02">2</option>
                  <option value="03">3</option>
                  <option value="04">4</option>
                  <option value="05">5</option>
                  <option value="06">6</option>
                  <option value="07">7</option>
                  <option value="08">8</option>
                  <option value="09">9</option>
                  <option value="10">10</option>
                  <option value="11">11</option>
                  <option value="12">12</option>
                  <option value="13">13</option>
                  <option value="14">14</option>
                  <option value="15">15</option>
                  <option value="16">16</option>
                  <option value="17">17</option>
                  <option value="18">18</option>
                  <option value="19">19</option>
                  <option value="20">20</option>
                  <option value="21">21</option>
                  <option value="22">22</option>
                  <option value="23">23</option>
                  <option value="24">24</option>
                  <option value="25">25</option>
                  <option value="26">26</option>
                  <option value="27">27</option>
                  <option value="28">28</option>
                  <option value="29">29</option>
                  <option value="30">30</option>
                  <option value="31">31</option>
                </select>
              </div> <?php } ?>

1 个答案:

答案 0 :(得分:0)

好的,因此有几个原因导致您遇到下拉菜单无法正确禁用的问题:

首先我注意到如果您提供的js代码保留原样,以下调用将返回一个空数组:

let select_month = document.querySelector('select[name="month'+x+'"]');

这可能会因您的浏览器呈现DOM并运行脚本的速度而改变,但一般来说,运行for循环时无法保证选择菜单和选项。因此,您应该使用DOMContentLoaded事件(要遵循的代码)包装main for循环,以确保它仅在加载DOM后运行。如果您正在使用jQuery,您可以将整个事物包装在$(document).ready(...)函数中。

其次,您可以替换以下行:

let day_arr = [].slice.call(document.querySelectorAll('select[name="day'+x+'"] option'))

只有:

let day_arr = document.querySelectorAll('select[name="day'+idx+'"] option');

querySelectorAll已经返回了一个可以迭代的数组。您还会注意到我有变量idx而不是x。这是由于第三个更令人困惑的原因。

在javascript中使用var声明的变量不是块作用域(您可以在es6中使用let来避免这种情况),它们的作用域是定义它们的函数,或者全局作用域。这意味着在你的main for循环中:for(x=1;x<=20;x++)变量的范围没有被定义为在for循环块中,就像c / c ++,java,c#这样的语言,它被定义为包围它的功能范围。在这种特殊情况下,没有包含它的功能,因此它与全局对象相似。因此,结果如下:

当for循环运行时,它会遍历该块并执行所有代码,其中一个代码正在注册此事件监听器:

select_month.addEventListener('change', function(e){})

此事件侦听器是一个异步调用,这意味着它在for循环运行时不会被执行,它只被声明并注册到一个事件。这意味着当它最终运行时,在更改事件之后,for循环早已完成。但是,此更改事件函数在以下行中引用了x

document.querySelectorAll('select[name="day'+x+'"] option')

当它最终运行这段代码时,它在当前时间获得x的值,就在for循环完成之后,这意味着它的值为21。它在循环完成后可以访问x值,因为正如我们所讨论的那样,变量没有作用于for循环,它存在于它之外。因此,对于此事件的所有调用,x将为21,无论您选择哪个下拉列表。由于21不是有效日期名称的一部分,因此数组将始终为空。

解决方法是将change事件调用包装在自己的函数中,并将x的值作为参数传递给这个新函数,我们称之为idx。该函数将立即声明并执行。请注意,即使运行该函数,更改事件仍然只是注册,而不是执行。现在querySelector语句将引用函数参数而不是x变量。由于函数参数是针对每个循环执行的,因此每次都传递一个新值x,因此每个更改事件侦听器的参数也是新的。您之前可能已经看过这种模式,它被称为闭包。

我已经更新了你的代码,看看:

var data = {
  disabled_days: {
    "June": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 17, 18, 24, 25, 31],
    "July": [1, 2, 3, 4, 8, 9, 15, 16, 22, 23, 29, 30],
    "August": [5, 6, 12, 13, 19, 20, 26, 27]
  }
}
//On DOM Loaded
document.addEventListener("DOMContentLoaded", function(event) { 
    //loop through to create this 20 times
    for(x=1;x<=20;x++){
        //get the select month box
        let select_month = document.querySelector('select[name="month'+x+'"]');

        //A colusure to wrap the change event listener, this is done
        //to pass the current value of x into the listener
        (function(idx){
          //when month is selected
          select_month.addEventListener('change', function(e){

            //get the name of the month
            let month = select_month[select_month.selectedIndex].textContent;

            //get all the days in the data select box and enable them all
            let day_arr = document.querySelectorAll('select[name="day'+idx+'"] option');

            for(let day of day_arr) day.disabled = false;

            //loop over all the days to be disabled, find thme in the data ovject based on month and disable those days
            for (let disabled_day of data.disabled_days[month]){
                for (let day of day_arr){
                    if (day.textContent == disabled_day) day.disabled = true;
                }
            }    
          })
        })(x);
    }
});

最后,您应该重新考虑代码中的逻辑。它有很多低效和不必要的部分,特别是在事件监听器中。您当前正在调用2个for循环,其中一个是嵌套的,每次在20个下拉列表中的任何一个上选择一个月。没有必要这样做,你应该只在加载DOM时禁用相关选项,并且永远不必在更改事件上循环它。您必须考虑数据的状态以及改变它的事件;选择月份下拉列表对禁用的天数对象没有影响,因此您不必更新任何依赖此数据的元素。