如何禁用/启用表单或多个表单元素之间的选择选项?

时间:2019-05-05 15:34:24

标签: javascript html forms

我正在研究多个组选择的基本概念,其中一些输入选项由于当前通常不可用而被禁用,而其他输入选项由于已经在其他地方被选择而被禁用。如示例中所示,这可以是两种或多种形式,也可以是一种形式内的多个元素。我需要链接选择的数值输出并在单独的元素之间禁用/启用选项。

我们使用PHP通过服务器端php include按字母顺序生成选项列表。我只保留了静态html来测试而忽略了php部分。

该示例显示了两个多重选择表单输入。团队成员在后台以数字标识,在UX中以人名标识。现在可以看到正常隐藏的数字输出,以便在每个团队下面进行测试。

我想在选择下一个团队时简单地从隐藏的数字输出onBlur和onFocus中预先检查已选择的值。然而,由于表单元素已由multi.js接管,因此不再触发Focus和Blur事件。

基于Fabian Lindfors的multi.min.css的CSS:

/* basic styling */
body { font-family: Avenir,sans-serif; }
.container { box-sizing: border-box; margin: 0 auto; max-width: 500px; padding: 0 20px; width: 100%; }
.developer { color:#999; font-size: small; margin-bottom:20px }
/* form and selection styling */
label{ margin-left:20px; color: #666; font-weight:bolder }
.multi-wrapper{ border: 1px solid #999; border-radius: 8px; width: 450px; margin:10px 0 10px 0 }
.multi-wrapper .non-selected-wrapper,.multi-wrapper .selected-wrapper{ box-sizing: border-box; display: inline-block; height: 150px; overflow-y: scroll; padding: 10px; vertical-align: top; width: 50% }
.multi-wrapper .non-selected-wrapper{ background: #fafafa; border-radius: 0 0 0 8px; border-right: 1px solid #ccc }
.multi-wrapper .selected-wrapper{ background: #FFF; border-radius: 0 0 8px 0 }
.multi-wrapper .header{ color: #4f4f4f; cursor: default; font-weight: 700; margin-bottom: 5px; padding: 5px 10px }
.multi-wrapper .item{ cursor: pointer; display: block; padding: 5px 10px }
.multi-wrapper .item: hover{ background: #ececec; border-radius: 2px }
.multi-wrapper .item-group{ padding: 5px 10px }
.multi-wrapper .item-group .group-label{ display: block; font-size: .875rem; opacity: .5; padding: 5px 0 }
.multi-wrapper .search-input{ border: 0; border-bottom: 1px solid #ccc; border-radius: 8px 8px 0 0; display: block; font-size: 1em; margin: 0; outline: 0; padding: 10px 20px; width: 100%; box-sizing: border-box }
.multi-wrapper .non-selected-wrapper .item.selected{ display:  none; opacity: .5 }
.multi-wrapper .non-selected-wrapper .item.disabled,.multi-wrapper .selected-wrapper .item.disabled{ opacity: .5; text-decoration: line-through }
.multi-wrapper .non-selected-wrapper .item.disabled: hover,.multi-wrapper .selected-wrapper .item.disabled: hover{ background: inherit; cursor: inherit}

HTML表单:

<div class="container">
    <h1>team selection demo</h1>
    <form>
        <label for="team_1">Select members for day shift</label>
        <select onChange="reportUpdatedValues(this,this.name);" 
            multiple="multiple"
            name="team_1"
            id="team_1_select">
        <option value="13" disabled="disabled">Alex</option>
        <option value="1">Bob</option>
        <option value="8">Diana</option>
        <option value="5">Frank</option>
        <option value="9">Fred</option>
        <option value="11">Helen</option>
        <option value="10">Jeanne</option>
        <option value="4">Linda</option>
        <option value="3">Mary</option>
        <option value="2" disabled="disabled">Max</option>
        <option value="7">Mo</option>
        <option value="6">Paul</option>
        <option value="12">Sara</option>
        </select>
        <span class="developer" style="display:inherit; padding:10px 0 10px 20px">
            normally hidden digital output:
        <input id="output_team_1" type="text" style="float:right">
        </span>
        <label for="team_2">Select members for night shift</label>
        <select onChange="reportUpdatedValues(this,this.name);" 
            multiple="multiple"
            name="team_2"
            id="team_2_select">
        <option value="13" disabled="disabled">Alex</option>
        <option value="1">Bob</option>
        <option value="8">Diana</option>
        <option value="5">Frank</option>
        <option value="9">Fred</option>
        <option value="11">Helen</option>
        <option value="10">Jeanne</option>
        <option value="4">Linda</option>
        <option value="3">Mary</option>
        <option value="2" disabled="disabled">Max</option>
        <option value="7">Mo</option>
        <option value="6">Paul</option>
        <option value="12">Sara</option>
        </select>
    </form>
    <span class="developer" style="display:inherit; padding:10px 0 10px 20px">
        normally hidden digital output:
        <input id="output_team_2" type="text" style="float:right">
    </span>
</div>

javascript

// initialise multi, set headers for group 1
       var select = document.getElementById("team_1_select");
       multi(select, {
           non_selected_header: "Candidates",
           selected_header: "Team 1"
       });

// initialise multi, set headers for group 2
       var select = document.getElementById("team_2_select");
       multi(select, {
           non_selected_header: "Candidates",
           selected_header: "Team 2"
       });

function reportUpdatedValues(element,team){
// Return an array of the selected options in element
      var result = [];
      var options = element && element.options;
      var opt;

      for (var i=0, iLen=options.length; i<iLen; i++) {
        opt = options[i];

        if (opt.selected) {
          result.push(opt.value || opt.text);
        }
      }
      // for development purpose only we display the result in the team output
      document.getElementById('output_'+team).value = result;
      return result;
}

/*! multi.min.js version 03-12-2018 by Fabian Lindfors */
var multi=function(){var e=function(e,t,n){var a=e.options[t.target.getAttribute("multi-index")];if(!a.disabled){a.selected=!a.selected;var i,d,r,l=n.limit;if(l>-1){for(var s=0,o=0;o<e.options.length;o++)e.options[o].selected&&s++;if(s===l){this.disabled_limit=!0,"function"==typeof n.limit_reached&&n.limit_reached();for(o=0;o<e.options.length;o++){(c=e.options[o]).selected||c.setAttribute("disabled",!0)}}else if(this.disabled_limit){for(o=0;o<e.options.length;o++){var c;"false"===(c=e.options[o]).getAttribute("data-origin-disabled")&&c.removeAttribute("disabled")}this.disabled_limit=!1}}i="change",d=e,(r=document.createEvent("HTMLEvents")).initEvent(i,!1,!0),d.dispatchEvent(r)}},t=function(e,t){if(e.wrapper.selected.innerHTML="",e.wrapper.non_selected.innerHTML="",t.non_selected_header&&t.selected_header){var n=document.createElement("div"),a=document.createElement("div");n.className="header",a.className="header",n.innerText=t.non_selected_header,a.innerText=t.selected_header,e.wrapper.non_selected.appendChild(n),e.wrapper.selected.appendChild(a)}if(e.wrapper.search)var i=e.wrapper.search.value;for(var d=null,r=null,l=0;l<e.options.length;l++){var s=e.options[l],o=s.value,c=s.textContent||s.innerText,p=document.createElement("a");if(p.tabIndex=0,p.className="item",p.innerHTML=c,p.setAttribute("role","button"),p.setAttribute("data-value",o),p.setAttribute("multi-index",l),s.disabled&&(p.className+=" disabled"),s.selected){p.className+=" selected";var u=p.cloneNode(!0);e.wrapper.selected.appendChild(u)}if("OPTGROUP"==s.parentNode.nodeName&&s.parentNode!=r){if(r=s.parentNode,(d=document.createElement("div")).className="item-group",s.parentNode.label){var m=document.createElement("span");m.innerHTML=s.parentNode.label,m.className="group-label",d.appendChild(m)}e.wrapper.non_selected.appendChild(d)}s.parentNode==e&&(d=null,r=null),(!i||i&&c.toLowerCase().indexOf(i.toLowerCase())>-1)&&(null!=d?d.appendChild(p):e.wrapper.non_selected.appendChild(p))}};return function(n,a){if((a=void 0!==a?a:{}).enable_search=void 0===a.enable_search||a.enable_search,a.search_placeholder=void 0!==a.search_placeholder?a.search_placeholder:"Search...",a.non_selected_header=void 0!==a.non_selected_header?a.non_selected_header:null,a.selected_header=void 0!==a.selected_header?a.selected_header:null,a.limit=void 0!==a.limit?parseInt(a.limit):-1,isNaN(a.limit)&&(a.limit=-1),null==n.dataset.multijs&&"SELECT"==n.nodeName&&n.multiple){n.style.display="none",n.setAttribute("data-multijs",!0);var i=document.createElement("div");if(i.className="multi-wrapper",a.enable_search){var d=document.createElement("input");d.className="search-input",d.type="text",d.setAttribute("placeholder",a.search_placeholder),d.addEventListener("input",function(){t(n,a)}),i.appendChild(d),i.search=d}var r=document.createElement("div");r.className="non-selected-wrapper";var l=document.createElement("div");l.className="selected-wrapper",i.addEventListener("click",function(t){t.target.getAttribute("multi-index")&&e(n,t,a)}),i.addEventListener("keypress",function(t){var i=32===t.keyCode||13===t.keyCode;t.target.getAttribute("multi-index")&&i&&(t.preventDefault(),e(n,t,a))}),i.appendChild(r),i.appendChild(l),i.non_selected=r,i.selected=l,n.wrapper=i,n.parentNode.insertBefore(i,n.nextSibling);for(var s=0;s<n.options.length;s++){var o=n.options[s];o.setAttribute("data-origin-disabled",o.disabled)}t(n,a),n.addEventListener("change",function(){t(n,a)})}}}();"undefined"!=typeof jQuery&&function(e){e.fn.multi=function(t){return t=void 0!==t?t:{},this.each(function(){var n=e(this);multi(n.get(0),t)})}}(jQuery);

在该示例中,名称Alex和Max处于休假状态,因此PHP已经使它们通常不适用于所有团队。现在,如果为团队1选择了戴安娜(数字成员8),则对于团队2,团队3,团队4等,应该禁用或隐藏戴安娜选项。团队总数可能因情况而异。

2 个答案:

答案 0 :(得分:0)

我添加了以下旧式js函数以处理至少一部分问题,现在我删除了multi.js UX初始化以简化此情况。因此,现在在第1团队中选择的成员将被禁止在第2团队中进行选择,反之亦然,而不会启用一开始通常被禁用的成员。

但是,剩下的问题仍然是使用multi.js时该对象如何侦听其他对象的更改。尚未解决。

这也不是中央处理程序,因此它不应该获得精美解决方案的一等奖。

添加的JavaScript:

resize_textarea(document.getElementById('team_1_select'));
resize_textarea(document.getElementById('team_2_select'));

function disableOptionsForElement(array,element){
    // convert to Array if argument 1 is not array
    if(Array.isArray(array)==false){array = array.split(",");}
      // loop through values in array
      for (var i=0, iLen=array.length; i<iLen; i++) {
        // look for first option in element with current value
        var opt = getOptionByValue(element,array[i]);
        if (opt) { 
            // only disable option on target if value exists
            opt.disabled = true;
            opt.selected = false;
        }
      }
}

function enableOptionsForElement(array,element){
    // convert to Array if argument 1 is not array
    if(Array.isArray(array)==false){array = array.split(",");}
      // loop through values in array
      for (var i=0, iLen=array.length; i<iLen; i++) {
        // look for first option in element with current value
        var opt = getOptionByValue(element,array[i]);
        if (opt) { 
            // only enable option on target if value exists
            opt.disabled = false;
        }
      }
}

function parseSelectedValues(element,target){
// Return an array of the selected options in element
// target is optional DOMElement for output
      var result = [];
      var options = element && element.options;
      var opt;
      for (var i=0, iLen=options.length; i<iLen; i++) {
        var opt = options[i];
        if (opt.selected) {
          result.push(opt.value || opt.text);
        }
      }
      if( (typeof target === "object") && (target !== null) ){
        target.value = result;
      }
      return result;
}

function parseEnabledValues(element){
// Return an array of the selected options in element
      var result = [];
      var options = element && element.options;
      var opt;

      for (var i=0, iLen=options.length; i<iLen; i++) {
        var opt = options[i];

        if (opt.disabled==false) {
          result.push(opt.value || opt.text);
        }
      }
      return result;
}

function getOptionByValue(element,val){
// Return only a menu option element that has the requested value
      var options = element && element.options;
      var opt;

      for (var i=0, iLen=options.length; i<iLen; i++) {
        opt = options[i];

        if (opt.value===val) {
          return opt;
        }
      }
      return false;
}

function resize_textarea(area) {
//auto expand textarea to fit new number of lines
    area.style.height = (6+area.scrollHeight)+"px";
}

HTML进行了相应更改

<div class="container">
    <h2>team selection demo</h2>
    <form>
        <label for="team_1">Pick your team</label>
        <select onChange="
                this.form.output1.value = parseSelectedValues(this);
                enableOptionsForElement ( parseEnabledValues(  this ) , this.form.team_2_select );
                disableOptionsForElement( parseSelectedValues( this ) , this.form.team_2_select );
                " 
                multiple="multiple"
                name="team_1"
                id="team_1_select"
                class="multi-wrapper">
        <option value="13" disabled="disabled">Alex</option>
        <option value="1">Bob</option>
        <option value="8">Diana</option>
        <option value="5">Frank</option>
        <option value="9">Fred</option>
        <option value="11">Helen</option>
        <option value="10">Jeanne</option>
        <option value="4">Linda</option>
        <option value="3">Mary</option>
        <option value="2" disabled="disabled">Max</option>
        <option value="7">Mo</option>
        <option value="6">Paul</option>
        <option value="12">Sara</option>
        </select>
        <span class="developer" style="display:inherit; padding:10px 0 10px 20px">
            normally hidden digital output:
            <input id="output1" type="text" value="" style="float:right" readonly >
        </span>
        <!-- end of Team 1 -->
        <label for="team_2">Pick the other team</label>
        <select onChange="
                parseSelectedValues(this, this.form.output2);
                enableOptionsForElement ( parseEnabledValues(  this ) , this.form.team_1_select );
                disableOptionsForElement( parseSelectedValues( this ) , this.form.team_1_select );
                " 
                multiple="multiple"
                name="team_2"
                id="team_2_select"
                class="multi-wrapper">
        <option value="13" disabled="disabled">Alex</option>
        <option value="1">Bob</option>
        <option value="8">Diana</option>
        <option value="5">Frank</option>
        <option value="9">Fred</option>
        <option value="11">Helen</option>
        <option value="10">Jeanne</option>
        <option value="4">Linda</option>
        <option value="3">Mary</option>
        <option value="2" disabled="disabled">Max</option>
        <option value="7">Mo</option>
        <option value="6">Paul</option>
        <option value="12">Sara</option>
        </select>
        <span class="developer" style="display:inherit; padding:10px 0 10px 20px">
            normally hidden digital output:
            <input id="output2" type="text" value="" style="float:right" readonly >
        </span>
        <!-- end of Team 2 -->
    </form>
</div>

答案 1 :(得分:0)

fardhana在GitHub上找到了可以接受的解决方法,可以通过dispatchEvent()刷新multi.js。

因此,我添加了一个计时器,以使自定义UX每800毫秒刷新一次。现在,select元素可以由multi.js接管,并且该元素中以编程方式更改的值将以可接受的最小延迟显示给用户。

// initialise multi, set headers for group 1 and add auto refresh timer
 var team1 = document.getElementById('team_1_select');
 multi(team1, {
  non_selected_header: "Candidates",
  selected_header: "Team A"
});
 var timer1 = function(){ team1.dispatchEvent(new Event('change')); };
 setInterval(timer1, 800);

// initialise multi, set headers for group 2 and add auto refresh timer
 var team2 = document.getElementById('team_2_select');
 multi(team2, {
  non_selected_header: "Candidates",
  selected_header: "Team B"
});
 var timer2 = function(){ team2.dispatchEvent(new Event('change')); };
 setInterval(timer2, 800);

此解决方案基于https://github.com/fabianlindfors/multi.js/issues/31