如何制作HTML多选操作,就像按下控制按钮一样

时间:2021-07-29 21:16:34

I have a web application I'm working on that requires a HTML multiple select element operates like the control key is held down at all times. I.e. click on an option will toggle whether it is selected or not.

我有一个我正在处理的Web应用程序,它需要一个HTML多选元素,就像控制键一直处于按下状态一样。即点击一个选项将切换是否被选中。

I have tried various Jquery event binds but it appears that the standard HTML multiple select behavior executes before the Jquery bound event fires, making it all but impossible to simply intercept the event and modify the standard behavior.

我已经尝试了各种Jquery事件绑定,但看起来标准的HTML多选行为在Jquery绑定事件触发之前执行,使得几乎不可能简单地拦截事件并修改标准行为。

How can I make is so that single clicks will toggle the selection of an individual option?

我怎样才能使单击会切换单个选项的选择?

4 个解决方案

#1


18  

Try this out. You can store the option values in an object and use the click action to update the object then apply the changes to the select.

试试吧。您可以将选项值存储在对象中,并使用单击操作更新对象,然后将更改应用于选择。

Demo

http://jsfiddle.net/iambriansreed/BSdxE/

HTML

<select class="select-toggle" multiple="multiple">
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
</select>​

JavaScript

$('.select-toggle').each(function(){    
    var select = $(this), values = {};    
    $('option',select).each(function(i, option){
        values[option.value] = option.selected;        
    }).click(function(event){        
        values[this.value] = !values[this.value];
        $('option',select).each(function(i, option){            
            option.selected = values[option.value];        
        });    
    });
});​

#2


5  

You may want to consider a simpler solution, like using a list of checkboxes inside a div whose overflow property is set to scroll. That might work out better for you. Getting a drop down to do what you've asked is a bit involved.

您可能需要考虑一个更简单的解决方案,例如使用其overflow属性设置为scroll的div内的复选框列表。这对你来说可能更好。得到一个下拉来做你所问的有点涉及。

See this fiddle for example

例如,请看这个小提琴

HTML

<div id="container">
    <label><input type="checkbox" name="test" value="1" />Option 1</label>
    <label><input type="checkbox" name="test" value="2" />Option 2</label>
    <label><input type="checkbox" name="test" value="3" />Option 3</label>
    <label><input type="checkbox" name="test" value="4" />Option 4</label>
    <label><input type="checkbox" name="test" value="5" />Option 5</label>
    <label><input type="checkbox" name="test" value="6" />Option 6</label>
    <label><input type="checkbox" name="test" value="7" />Option 7</label>
    <label><input type="checkbox" name="test" value="8" />Option 8</label>
    <label><input type="checkbox" name="test" value="9" />Option 9</label>
    <label><input type="checkbox" name="test" value="10" />Option 10</label>
    <label><input type="checkbox" name="test" value="11" />Option 11</label>
    <label><input type="checkbox" name="test" value="12" />Option 12</label>
</div>

CSS

label{display:block;}
#container{height:100px;width:200px;overflow:scroll;}

#3


2  

Had to solve this problem myself and noticed the bugged behavior a simple interception of the mousedown and setting the attribute would have, so made a override of the select element and it works good.

不得不自己解决这个问题,并注意到窃听行为是对mousedown的简单拦截以及设置属性所具有的,因此对select元素进行了覆盖并且它运行良好。

jsFiddle: http://jsfiddle.net/51p7ocLw/

Note: This code does fix buggy behavior by replacing the select element in the DOM. This is a bit agressive and will break event handlers you might have attached to the element.

注意:此代码通过替换DOM中的select元素来修复错误行为。这有点激进,会破坏您可能附加到元素的事件处理程序。

window.onmousedown = function (e) {
    var el = e.target;
    if (el.tagName.toLowerCase() == 'option' && el.parentNode.hasAttribute('multiple')) {
        e.preventDefault();

        // toggle selection
        if (el.hasAttribute('selected')) el.removeAttribute('selected');
        else el.setAttribute('selected', '');

        // hack to correct buggy behavior
        var select = el.parentNode.cloneNode(true);
        el.parentNode.parentNode.replaceChild(select, el.parentNode);
    }
}
<h4>From</h4>

<div>
    <select name="sites-list" size="7" multiple>
        <option value="site-1">SITE</option>
        <option value="site-2" selected>SITE</option>
        <option value="site-3">SITE</option>
        <option value="site-4">SITE</option>
        <option value="site-5">SITE</option>
        <option value="site-6" selected>SITE</option>
        <option value="site-7">SITE</option>
        <option value="site-8">SITE</option>
        <option value="site-9">SITE</option>
    </select>
</div>

#4


0  

The above solutions all have the major downside of not supporting selections in the checkbox, which is actually the reason I'd use <select>-boxes over checkbox-inputs in the first place. Initially I visited this page to find a built-in way to fix this, but considering the above answers, I'll just write my own code.

上面的解决方案都有不支持选中复选框的主要缺点,这实际上是我首先在复选框输入上使用

The following code also has the advantage that it doesn't update on mouseup as opposed to the current top answer by iambriansreed, but on mousedown, which is the default behaviour in browsers.

下面的代码还有一个优点,它不会在mouseup上更新而不是iambriansreed当前的最高答案,而是在mousedown上,这是浏览器中的默认行为。

Just to help out future googlers, my code below:

只是为了帮助未来的googlers,我的代码如下:

// Copyright (c) 2018 Joeytje50. All rights reserved.
// This code is licenced under GNU GPL3+; see <http://www.gnu.org/licenses/>

// Source code and most recent version:
// https://gist.github.com/Joeytje50/2b73f9ac47010e7fdc5589788b80af77

// select all <options> within `sel` in the range [from,to]
// change their state to the state sel.options[from] is in,
// ie. change it to the current state of the first selected element
function selectRange(sel, from, to) {
  var toState = sel.options[from].selected;
  // make sure from < to:
  if (from > to) {
    var temp = from;
    from = to;
    to = temp;
  }
  if (!(sel instanceof HTMLSelectElement)) {
    throw new TypeError('selectRange requires a single non-jQuery select-element as first parameter');
  }
  // (de)select every element
  for (var i=from; i<=to; i++) {
    sel.options[i].selected = toState;
  }
}

$(function() {
  $('.addSelect').data('select-start', 0); // default selection start
  $('.addSelect').on('mousedown', function(e) {
    $(this).focus();
    // clicking on the edge of the <select> shouldn't do anything special
    if (!$(e.target).is('option')) return;
    // if Ctrl is pressed, just let the built-in functionality take over
    if (e.ctrlKey) return;
    // keep everything selected that's not affected by the range within a shift-click
    if (e.shiftKey) {
      var fromIdx = $(this).data('select-start')
      selectRange(this, $(this).data('select-start'), e.target.index);
      e.preventDefault();
      return false;
    }
    // save the starting <option> and the state to change to
    $(this).data('select-start', e.target.index);
    e.target.selected = !e.target.selected;
    e.preventDefault();
    // save a list of selected elements, to make sure only the selected <options>
    // are added or removed when dragging
    var selected = [];
    for (var i=0;i<this.selectedOptions.length;i++) {
      selected.push(this.selectedOptions[i].index);
    }
    $(this).data('selected', selected);
    $(this).children('option').on('mouseenter', function(e) {
      var sel = this.parentElement;
      // first reset all options to the original state
      for (var i=0;i<sel.options.length;i++) {
        if ($(sel).data('selected').indexOf(i) == -1) {
          sel.options[i].selected = false;
        } else {
          sel.options[i].selected = true;
        }
      }
      // then apply the new range to the elements
      selectRange(sel, $(sel).data('select-start'), e.target.index);
    });
  });
  // clean up events after click event has ended.
  $(window).on('mouseup', function() {
    $('.addSelect').children('option').off('mouseenter'); // remove mouseenter-events
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select class="addSelect" multiple size="10">
<option value="0">Foo0</option>
<option value="1">Bar1</option>
<option value="2">Baz2</option>
<option value="3">Lol3</option>
<option value="4">Heh4</option>
<option value="5">Hey5</option>
<option value="6">Meh6</option>
<option value="7">Xcq7</option>
<option value="8">Hmm8</option>
<option value="9">End9</option>
</select>

Feel free to use this code by simply copying it to any project you'd like to use it on.

只需将其复制到您想要使用它的任何项目,即可随意使用此代码。

#1


18  

Try this out. You can store the option values in an object and use the click action to update the object then apply the changes to the select.

试试吧。您可以将选项值存储在对象中,并使用单击操作更新对象,然后将更改应用于选择。

Demo

http://jsfiddle.net/iambriansreed/BSdxE/

HTML

<select class="select-toggle" multiple="multiple">
    <option>1</option>
    <option>2</option>
    <option>3</option>
    <option>4</option>
</select>​

JavaScript

$('.select-toggle').each(function(){    
    var select = $(this), values = {};    
    $('option',select).each(function(i, option){
        values[option.value] = option.selected;        
    }).click(function(event){        
        values[this.value] = !values[this.value];
        $('option',select).each(function(i, option){            
            option.selected = values[option.value];        
        });    
    });
});​

#2


5  

You may want to consider a simpler solution, like using a list of checkboxes inside a div whose overflow property is set to scroll. That might work out better for you. Getting a drop down to do what you've asked is a bit involved.

您可能需要考虑一个更简单的解决方案,例如使用其overflow属性设置为scroll的div内的复选框列表。这对你来说可能更好。得到一个下拉来做你所问的有点涉及。

See this fiddle for example

例如,请看这个小提琴

HTML

<div id="container">
    <label><input type="checkbox" name="test" value="1" />Option 1</label>
    <label><input type="checkbox" name="test" value="2" />Option 2</label>
    <label><input type="checkbox" name="test" value="3" />Option 3</label>
    <label><input type="checkbox" name="test" value="4" />Option 4</label>
    <label><input type="checkbox" name="test" value="5" />Option 5</label>
    <label><input type="checkbox" name="test" value="6" />Option 6</label>
    <label><input type="checkbox" name="test" value="7" />Option 7</label>
    <label><input type="checkbox" name="test" value="8" />Option 8</label>
    <label><input type="checkbox" name="test" value="9" />Option 9</label>
    <label><input type="checkbox" name="test" value="10" />Option 10</label>
    <label><input type="checkbox" name="test" value="11" />Option 11</label>
    <label><input type="checkbox" name="test" value="12" />Option 12</label>
</div>

CSS

label{display:block;}
#container{height:100px;width:200px;overflow:scroll;}

#3


2  

Had to solve this problem myself and noticed the bugged behavior a simple interception of the mousedown and setting the attribute would have, so made a override of the select element and it works good.

不得不自己解决这个问题,并注意到窃听行为是对mousedown的简单拦截以及设置属性所具有的,因此对select元素进行了覆盖并且它运行良好。

jsFiddle: http://jsfiddle.net/51p7ocLw/

Note: This code does fix buggy behavior by replacing the select element in the DOM. This is a bit agressive and will break event handlers you might have attached to the element.

注意:此代码通过替换DOM中的select元素来修复错误行为。这有点激进,会破坏您可能附加到元素的事件处理程序。

window.onmousedown = function (e) {
    var el = e.target;
    if (el.tagName.toLowerCase() == 'option' && el.parentNode.hasAttribute('multiple')) {
        e.preventDefault();

        // toggle selection
        if (el.hasAttribute('selected')) el.removeAttribute('selected');
        else el.setAttribute('selected', '');

        // hack to correct buggy behavior
        var select = el.parentNode.cloneNode(true);
        el.parentNode.parentNode.replaceChild(select, el.parentNode);
    }
}
<h4>From</h4>

<div>
    <select name="sites-list" size="7" multiple>
        <option value="site-1">SITE</option>
        <option value="site-2" selected>SITE</option>
        <option value="site-3">SITE</option>
        <option value="site-4">SITE</option>
        <option value="site-5">SITE</option>
        <option value="site-6" selected>SITE</option>
        <option value="site-7">SITE</option>
        <option value="site-8">SITE</option>
        <option value="site-9">SITE</option>
    </select>
</div>

#4


0  

The above solutions all have the major downside of not supporting selections in the checkbox, which is actually the reason I'd use <select>-boxes over checkbox-inputs in the first place. Initially I visited this page to find a built-in way to fix this, but considering the above answers, I'll just write my own code.

上面的解决方案都有不支持选中复选框的主要缺点,这实际上是我首先在复选框输入上使用

The following code also has the advantage that it doesn't update on mouseup as opposed to the current top answer by iambriansreed, but on mousedown, which is the default behaviour in browsers.

下面的代码还有一个优点,它不会在mouseup上更新而不是iambriansreed当前的最高答案,而是在mousedown上,这是浏览器中的默认行为。

Just to help out future googlers, my code below:

只是为了帮助未来的googlers,我的代码如下:

// Copyright (c) 2018 Joeytje50. All rights reserved.
// This code is licenced under GNU GPL3+; see <http://www.gnu.org/licenses/>

// Source code and most recent version:
// https://gist.github.com/Joeytje50/2b73f9ac47010e7fdc5589788b80af77

// select all <options> within `sel` in the range [from,to]
// change their state to the state sel.options[from] is in,
// ie. change it to the current state of the first selected element
function selectRange(sel, from, to) {
  var toState = sel.options[from].selected;
  // make sure from < to:
  if (from > to) {
    var temp = from;
    from = to;
    to = temp;
  }
  if (!(sel instanceof HTMLSelectElement)) {
    throw new TypeError('selectRange requires a single non-jQuery select-element as first parameter');
  }
  // (de)select every element
  for (var i=from; i<=to; i++) {
    sel.options[i].selected = toState;
  }
}

$(function() {
  $('.addSelect').data('select-start', 0); // default selection start
  $('.addSelect').on('mousedown', function(e) {
    $(this).focus();
    // clicking on the edge of the <select> shouldn't do anything special
    if (!$(e.target).is('option')) return;
    // if Ctrl is pressed, just let the built-in functionality take over
    if (e.ctrlKey) return;
    // keep everything selected that's not affected by the range within a shift-click
    if (e.shiftKey) {
      var fromIdx = $(this).data('select-start')
      selectRange(this, $(this).data('select-start'), e.target.index);
      e.preventDefault();
      return false;
    }
    // save the starting <option> and the state to change to
    $(this).data('select-start', e.target.index);
    e.target.selected = !e.target.selected;
    e.preventDefault();
    // save a list of selected elements, to make sure only the selected <options>
    // are added or removed when dragging
    var selected = [];
    for (var i=0;i<this.selectedOptions.length;i++) {
      selected.push(this.selectedOptions[i].index);
    }
    $(this).data('selected', selected);
    $(this).children('option').on('mouseenter', function(e) {
      var sel = this.parentElement;
      // first reset all options to the original state
      for (var i=0;i<sel.options.length;i++) {
        if ($(sel).data('selected').indexOf(i) == -1) {
          sel.options[i].selected = false;
        } else {
          sel.options[i].selected = true;
        }
      }
      // then apply the new range to the elements
      selectRange(sel, $(sel).data('select-start'), e.target.index);
    });
  });
  // clean up events after click event has ended.
  $(window).on('mouseup', function() {
    $('.addSelect').children('option').off('mouseenter'); // remove mouseenter-events
  });
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<select class="addSelect" multiple size="10">
<option value="0">Foo0</option>
<option value="1">Bar1</option>
<option value="2">Baz2</option>
<option value="3">Lol3</option>
<option value="4">Heh4</option>
<option value="5">Hey5</option>
<option value="6">Meh6</option>
<option value="7">Xcq7</option>
<option value="8">Hmm8</option>
<option value="9">End9</option>
</select>

Feel free to use this code by simply copying it to any project you'd like to use it on.

只需将其复制到您想要使用它的任何项目,即可随意使用此代码。