javascript - jQuery - 根据已克隆行的表单内的另一个选择将下拉列表更改为多选

标签 javascript jquery

我发现了讨论 using a multiselect based on another multiselect 的问题, cloning a form row ,和 dynamically updating select options 。但是,没有任何问题可以回答如何一起完成这三项工作。

在我的具体示例中,我有两个并排的下拉菜单 - operation_keyoperation_value。根据用户对 operation_key 的选择,他们应该看到常规(单)选择或多选,并且在相应的 operation_value 下拉列表中动态填充适当的选项。

以下是其工作方式的映射:

+---------------+---------------------------------+---------------------------------------+
| Operation Key | Operation Value - Dropdown Type |  Operation Value - Dropdown Options   |
+---------------+---------------------------------+---------------------------------------+
| continents    | multiple                        | North  America, South America, Europe |
| languages     | multiple                        | English, French, Spanish              |
| eye_color     | single                          | Brown, Blue, Green, Hazel             |
| age           | single                          | 18-24, 25-32, 33-40, >41              |
+---------------+---------------------------------+---------------------------------------+

我结合了各个示例中的代码并得出以下结果:

// clone functionality

const regex = /^(.+?)(\d+)$/i;
let cloneIndex = $(".operation").length;

function setIndex(elements) {
  elements.each(function(index, element) {
    // set the appropriate index on the cloned element (not working)
    $(element).find("select.operation_keys").attr("name", "operation_keys[" + index + "]");
    $(element).find("select.operation_values").attr("name", "operation_values[" + index + "]");
  });
}

function clone() {

  let $removeButton = $(this).closest(".actions").find(".remove-operation");
  let $closestOperation = $(this).closest(".actions").prev(".operation").first();

  $removeButton.show();

  $closestOperation.clone()
    .insertAfter($closestOperation).attr("id", "operation-" + cloneIndex)
    .find("*")
    .each(function() {
      let id = this.id || "";
      let match = id.match(regex) || [];
      if (match.length == 3) {
        this.id = match[1] + '-' + (cloneIndex);
      }
    });
  cloneIndex++;

  let $Operations = $(this).closest(".operation-parent").find(".operation");

  setIndex($Operations);
}

function remove() {
  $(this).closest(".actions").prev(".operation").remove();

  let $Operations = $(this).closest(".operation-parent").find(".operation");
  setIndex($Operations);
  let totalElements = $(this).closest(".operation-parent").find(".operation").length;
  if (totalElements === 1) {
    $(this).hide();
  }
  cloneIndex--;
}

$(".add-operation").on("click", clone);
$(".remove-operation").on("click", remove);

// select logic

function buildSelect(operationKey) {
  let result = {};
  switch (operationKey) {
    case 'continents':
      result['operationValues'] = ['North America', 'South America', 'Europe'];
      result['type'] = 'multiple';
      break;
    case 'languages':
      result['operationValues'] = ['English', 'French', 'Spanish'];
      result['type'] = 'multiple';
      break;
    case 'eye_color':
      result['operationValues'] = ['Brown', 'Blue', 'Green', 'Hazel'];
      result['type'] = 'single';
      break;
    case 'age':
      result['operationValues'] = ['18-24', '25-32', '33-40', '>41'];
      result['type'] = 'single';
      break;
  }
  return result;
}

$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  // get the nearest value dropdown element (not working)
  let valueSelect = $(this).closest('.operation-values');
  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple")
  }
  // populate the values  (not working)
  let options = result['operationValues'];
  for (var i = 0; i < options.length; i++) {
    valueSelect.options.add(new Option(option.toLowerCase(), option));
  }


});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/css/bootstrap-multiselect.css" type="text/css" />


<div class="form-group row">
  <label class="col-4">Operation Key</label>
  <label class="col-6">Operation Value</label>
</div>
<div class="form-group">

  <div class="operation-parent row">
    <div class="col-8 operation">
      <div class="row">
        <div class="col-6">
          <select class="form-control form-control-lg operation-keys" name="operation_keys[]">
            <option value="continents">Continents Visited</option>
            <option value="languages">Languages Spoken</option>
            <option value="eye_color">Eye Color</option>
            <option value="age">Age</option>
          </select>
        </div>

        <div class="col-6">
          <select class="form-control form-control-lg mb-30 operation-values" name="operation_values[]">

          </select>
        </div>

      </div>
    </div>
    <div class="col-2 actions">
      <a class="btn btn-alt-success add-operation">+</a>
      <a class="btn btn-alt-danger remove-operation" style="display:none;">-</a>
    </div>
  </div>

  <script type="text/javascript" src="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/js/bootstrap-multiselect.js"></script>

JSFiddle:https://jsfiddle.net/vs6oLugy/

有几个问题:

1) setIndex() 函数应该动态更新克隆的 .operation_keys.operation_values 字段的名称。例如,操作键[1]操作值[1]操作键[2]操作值[2]等等。由于某种原因,它不起作用。我认为这是因为 $(element).find("select.operation_keys") 没有正确定位该元素,但我不明白为什么不是。

2)使用适当的类型(常规选择或多选)查找并更新相应的 .operation_values 下拉列表并设置可用选择/选项的其余逻辑不起作用。我认为问题在于要修改的下拉列表的行:

let valueSelect = $(this).closest('.operation-values');

每个选择都位于父 .col-6 div 内,因此可能 $(this).closest() 无法向上遍历DOM 树?不过,我也不确定应该改变什么才能使这部分正常工作。我尝试过查看兄弟元素并首先获取父元素,但都没有成功。

3)当克隆一行时,我不确定如何隐藏第二个(最右边)下拉列表(operation_value)以鼓励用户在左侧下拉列表中进行选择(operation_key) 首先。当克隆一行时,从中克隆的行可能有两个可见的下拉菜单,因此在由该行创建的新的最右侧下拉菜单上调用 .hide() 是否足够安全? clone()函数?

非常感谢任何帮助!

最佳答案

在此代码中需要考虑一些事项:

  1. 您正在使用 jQuery。但是你将代码与普通 JS 混合在一起。
  2. 忘记了一些步骤,例如删除多个属性的 else 以及在添加新选项时清空选择。
  3. options 返回一个只读列表。
$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  let valueSelect = $('.operation-values');

  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple");
  }
  else
  {
    valueSelect.removeAttr("multiple", "multiple");
  }

  // populate the values
  let options = result['operationValues'];
  //make the list empty again
  valueSelect.empty();
  for (var i = 0; i < options.length; i++) {
    //use jQuery's append and add options through string!
    valueSelect.append(`<option value="${options[i].toLowerCase()}" >${options[i]}</option>`);
  }


});

// clone functionality

const regex = /^(.+?)(\d+)$/i;
let cloneIndex = $(".operation").length;

function setIndex(elements) {
  elements.each(function(index, element) {
    // set the appropriate index on the cloned element (not working)
    $(element).find("select.operation_keys").attr("name", "operation_keys[" + index + "]");
    $(element).find("select.operation_values").attr("name", "operation_values[" + index + "]");
  });
}

function clone() {

  let $removeButton = $(this).closest(".actions").find(".remove-operation");
  let $closestOperation = $(this).closest(".actions").prev(".operation").first();

  $removeButton.show();

  $closestOperation.clone()
    .insertAfter($closestOperation).attr("id", "operation-" + cloneIndex)
    .find("*")
    .each(function() {
      let id = this.id || "";
      let match = id.match(regex) || [];
      if (match.length == 3) {
        this.id = match[1] + '-' + (cloneIndex);
      }
    });
  cloneIndex++;

  let $Operations = $(this).closest(".operation-parent").find(".operation");

  setIndex($Operations);
}

function remove() {
  $(this).closest(".actions").prev(".operation").remove();

  let $Operations = $(this).closest(".operation-parent").find(".operation");
  setIndex($Operations);
  let totalElements = $(this).closest(".operation-parent").find(".operation").length;
  if (totalElements === 1) {
    $(this).hide();
  }
  cloneIndex--;
}

$(".add-operation").on("click", clone);
$(".remove-operation").on("click", remove);

// select logic

function buildSelect(operationKey) {
  let result = {};
  switch (operationKey) {
    case 'continents':
      result['operationValues'] = ['North America', 'South America', 'Europe'];
      result['type'] = 'multiple';
      break;
    case 'languages':
      result['operationValues'] = ['English', 'French', 'Spanish'];
      result['type'] = 'multiple';
      break;
    case 'eye_color':
      result['operationValues'] = ['Brown', 'Blue', 'Green', 'Hazel'];
      result['type'] = 'single';
      break;
    case 'age':
      result['operationValues'] = ['18-24', '25-32', '33-40', '>41'];
      result['type'] = 'single';
      break;
  }
  return result;
}

$('.operation-keys').on('change', function() {
  let operationKey = $(this).val()
  let valueSelect = $('.operation-values');
  
  // get the values + type of select we should populate
  let result = buildSelect(operationKey);
  // set the select type, if needed
  if (result['type'] == 'multiple') {
    valueSelect.attr("multiple", "multiple");
  }
  else
  {
    valueSelect.removeAttr("multiple", "multiple");
  }

  // populate the values
  let options = result['operationValues'];
  valueSelect.empty();
  for (var i = 0; i < options.length; i++) {
    valueSelect.append(`<option value="${options[i].toLowerCase()}" >${options[i]}</option>`);
  }


});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" href="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/css/bootstrap-multiselect.css" type="text/css" />


<div class="form-group row">
  <label class="col-4">Operation Key</label>
  <label class="col-6">Operation Value</label>
</div>
<div class="form-group">

  <div class="operation-parent row">
    <div class="col-8 operation">
      <div class="row">
        <div class="col-6">
          <select class="form-control form-control-lg operation-keys" name="operation_keys[]">
            <option value="continents">Continents Visited</option>
            <option value="languages">Languages Spoken</option>
            <option value="eye_color">Eye Color</option>
            <option value="age">Age</option>
          </select>
        </div>

        <div class="col-6">
          <select class="form-control form-control-lg mb-30 operation-values" name="operation_values[]">

          </select>
        </div>

      </div>
    </div>
    <div class="col-2 actions">
      <a class="btn btn-alt-success add-operation">+</a>
      <a class="btn btn-alt-danger remove-operation" style="display:none;">-</a>
    </div>
  </div>

  <script type="text/javascript" src="https://github.com/davidstutz/bootstrap-multiselect/blob/master/dist/js/bootstrap-multiselect.js"></script>

关于javascript - jQuery - 根据已克隆行的表单内的另一个选择将下拉列表更改为多选,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59920258/

相关文章:

javascript - 如何使用 Footable row-toggler 来切换数据

javascript - 当用户输入文本时,如何防止重新调整 div 的大小?

javascript - 如何使用 GreaseMonkey 删除/关闭页面上的“不可选择”?

javascript - 如何检测css背景url

javascript - 通过拖放 fullcalendar-Javascript 来更新事件

javascript - 如何隐藏 Jquery 中已使用 .load 函数加载的元素

javascript - 我可以在使用 jquery mouseout 后恢复值吗?

jquery - 将 jQueryUI 可调整大小小部件应用于动态创建的元素

javascript - 选中时触发复选框

javascript - 对象中的 graph.facebook 输出