我发现了讨论 using a multiselect based on another multiselect 的问题, cloning a form row ,和 dynamically updating select options 。但是,没有任何问题可以回答如何一起完成这三项工作。
在我的具体示例中,我有两个并排的下拉菜单 - operation_key
和 operation_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()
函数?
非常感谢任何帮助!
最佳答案
在此代码中需要考虑一些事项:
- 您正在使用 jQuery。但是你将代码与普通 JS 混合在一起。
- 忘记了一些步骤,例如删除多个属性的 else 以及在添加新选项时清空选择。
- 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/