【问题标题】:Show/Hide form fields based on value of other fields根据其他字段的值显示/隐藏表单字段
【发布时间】:2015-01-27 07:16:00
【问题描述】:

我正在动态生成具有不同字段(文本框、文本区域、选择、单选/复选框)的表单。

我想显示/隐藏一些字段,具体取决于在其他一些字段中选择的内容。

一个简单的案例可以是:

Fiddle Demo

$('select').change(function () {
    if($(this).val() === '1') {
        $('p').show();
    }
    else {
        $('p').hide();
    }
});
p {
    display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<select>
    <option value="0">Bike</option>
    <option value="1">Car</option>
</select>

<p>Left / right hand drive ?
    Left: <input type="radio" value="left" name="dir" />
    Right: <input type="radio" value="right" name="dir" />
</p>

我正在动态生成字段,并且在多种情况下,我需要根据其他一些字段的值显示/隐藏不同的字段。所以,我不想一次又一次地编写相同的代码。为了遵守 DRY 原则,我想创建某种通用函数(可能是插件)来处理所有这些场景。

我的基本(工作)想法是:

Fiddle Demo

$('[data-dependent]').each(function () {
    var $ele = $(this);
    var dependsOn = $ele.data('dependent');
    $.each(dependsOn, function (target, value) {
        $(target).on('change', function () {
            if ($(this).val() === value) {
                $ele.show();
            } else {
                $ele.hide();
            }
        });
    });
});
[data-dependent] {
    display: none;
}
<select id="vehicle">
  <option value="0">Bike</option>
  <option value="1">Car</option>
</select>

<p data-dependent='{"#vehicle": "1" }'>Left / right hand drive ? Left:
  <input type="radio" value="left" name="dir" />Right:
  <input type="radio" value="right" name="dir" />
</p>

此代码有效,但它附加了许多事件处理程序。此外,它不处理可以根据超过 1 个字段的值显示/隐藏字段的情况。我想不出一个很好的方法来处理这个问题。

我正在寻找一种干净且灵活的解决方案。如果已经有这个插件,那也可以。

【问题讨论】:

    标签: javascript jquery html css forms


    【解决方案1】:

    您听说过观察者模式吗?如果没有,link here 或者只是在 js 设计模式书中搜索它。观察者模式的特点是它在这些情况下非常有用且模块化(且高效),其中一个对象监听另一个对象的变化。

    正如我在您的问题中看到的,不一定需要观察者模式,因为仅当单击或选择 一个 对象时才会显示对象。但是,如果您只有选择两个单选按钮,那么只有在两个中显示,那么你真的需要它。

    因为你说你想要一个干净/灵活的解决方案,你需要一个设计模式的答案;)这是我的尝试: (skip the code and see the codepen i made for you)

    HTML:

    <!--
    1.what is data-publish?
    if data-publish gets clicked or changed(depends wheter is input or select option), it notifies every element in DOM with data-observe with the same value as our clicked data-publish.
    2.what is data-observe?
    data-observe="4 5" observes the DOM elements with data-publish="4" & data-publish="5" for a change. data-observe="4 5" gets display:block only when data-publish="4" & data-publish="5" are selected(it can be the case where data-publish="4 5", and it works the same)
    3. what is data-name?
    data-name is a unique name of a DOM element which as data-observe attribute. this is set at page load, js insert in DOM a random string to data-name
    -->
    <p>combinations:(note, at page load, nothing is selected)</p>
    <p>1 & 1</p>
    <p>2 & 2</p>
    <p>1 & 1 -> & 7 </p>
    <p>1 & 1 & 10 & 10 -> & 7</p>
    <select>
      <option selected></option>
      <option data-publish="1">pub 1</option>
      <option data-publish="2">pub 2</option>
      <option data-publish="3">pub 3</option>
    </select>
    <select>
        <option selected></option>
      <option data-publish="4">pub 4</option>
      <option data-publish="1">pub 1</option>
      <option data-publish="5">pub 5</option>
    </select>
    <select>
        <option selected></option>
      <option data-publish="10">pub 10</option>
      <option data-publish="11">pub 11</option>
      <option data-publish="12">pub 12</option>
    </select>
    <select>
        <option selected></option>
      <option data-publish="10 2">pub 10 & 2</option>
      <option data-publish="13">pub 13</option>
      <option data-publish="14">pub 14</option>
    </select>
    
    <p data-observe="1">(triggered by two 1)
      pub 7<input type="checkbox" data-publish="7">
      pub 8<input type="checkbox" data-publish="8">
      pub 9<input type="checkbox" data-publish="9">
    </p>
    <p data-observe="2">triggered by two 2</p>
    <p data-observe="1 7">i am triggered by two 1, one 7</p>
    <p data-observe="1 7 10">i am triggered by two 1, one 7, two 10</p>
    

    CSS:

    [data-observe] {
      display: none;
    }
    

    Javascript:

    /*
    * @author: Ali Cerrahoglu
    * @thing: awesome thing
    * @use: no defaults...so version 1.0.0
      it's a simple pub-sub which shows divs based on select options and radio/checkbox inputs
    */
    // revealing module pattern
    var ObserverPlugin = (function(){
    
        // here will be stored every DOM object which has
        // data-observe attr and data-name attr (data-name will be served
        //  as a key , which will store another object with a reference to the DOM object
        //  how many object does it observe)
        var observers = {};
        var publishers = [];
    
        // observer pattern & revealing module pattern
        var observer = (function(){
            var topics = {};
    
            var publish = function(topic, reference) {
                // if there is no topic on the publish call, well get out !
                if (!topics[topic]) {
                    return false;
                }
    
                // self invoked funciton, which calls the function passed when
                // the topic was subscribed (if more then one function was published on the same topic
                // then call each one of them)
                (function(){
                    var subscribers = topics[topic],
                        len = subscribers ? subscribers.length : 0;
    
                    while (len--) {
                        subscribers[len].func(topic, reference);
                    }
                })();
            };
    
            var subscribe = function(topic, func) {
                if (!topics[topic]) {
                    topics[topic] = [];
                }
    
                topics[topic].push({
                    func: func
                });
            };
    
            return {
                subscribe: subscribe,
                publish: publish,
           topics: topics
            }
        })();
    
    
      // creates random string, used to make data-name random for observers
      var _makeRandomString = function() {
        var text = "";
        var possible = "abcdefghijklmnopqrstuvwxyz0123456789";
    
        for( var i=0; i < 5; i++ ) {
            text += possible.charAt(Math.floor(Math.random() * possible.length));
        }
    
        return text;
      }
    
        // verifies if eleme existis in array, if not, returns false
        var _isInside = function( elem, array ) {
            return array.indexOf(elem) > -1;
        }
    
    
        // topic is the topic
        // reference is a reference to the DOM object clicked
        var _observerFunction = function(topic, reference) {
            var number = reference.attr('data-value');
            var topics = topic.toString().split(' ');
         var length = topics.length;
            for( var key in observers ) {
                for( var i = 0; i < length; i +=1 ) {
                    if( _isInside( topics[i], observers[key].topicsObserved ) ) {
                        // it exists
                        observers[key].sum += Number(number);
                        // 'number' is a string, so we have to convert it back to number
                    }
                }
                if( observers[key].sum == 0 ) {
                    // it is 0, so show that goddam DOM obj ! :))
                    // again, put here 'var' for clarity
                    // does not affect the code
                    var display = 'block';
                }
                else {
                    // it is not 0, so hide it
                    var display = 'none';
                }
                observers[key].reference.css('display', display);
            }
            // change value to -1 or 1
            if( number == '-1' ) {
                reference.attr('data-value', '1');
            }
            else {
                reference.attr('data-value', '-1');
            }
        }
    
        /*
        *   lets say we have 3 DOM objects with data-publish="1"
            and 2 DOM objects with data-publish="2"
            and one with data-observe="1 2";
            so data-observe has to be called 5 times in order for him to be shown on the page;
    
            each DOM object with data-publish will be added at runtime a data-value attribute
            which will be -1 or 1. each time it is clicked or changed, it changes to the opposite.
            this serves as data-observes will have a property named sum, which will be in the previous case 5
            5 gets calculated with -1, or 1 when clicked data-publish DOM object.
    
            So if i click first at data-publish="1" , 5 becomes 4. if i click again the same data-publish, becomes 5.
    
            when sum property becomes 0, the data-observe is shown.
    
            this function calculates how many data-publish="1" exists and so on
        (it also does the other stuff needed for publishers)
        */
        var _managePublishers = function() {
            $('[data-publish]').each(function(){
                var el = $(this);
                // adds that value data, remember it? :D
                el.attr('data-value', '-1');
                // trim in case data-publish = "1 2 3" and store in an array
                var publisher = el.data('publish').toString();
          // we subscripe 'publisher' topic, but we check each string in topic
          // here is tricky. if one publishers has more than one topic inside data-publish
          // then we subscribe topic, but we check for topic's substring in publishers
              var topics = publisher.split(' ');
           if( !observer.topics[publisher] ) {
              // we subscribe data-publish topic, becouse when we click it we want to fire something, no?
              observer.subscribe( publisher, _observerFunction );
           }
           // but here in publishers we add only the substrings
           for( var key in topics ) {
              if( publishers[topics[key]] ) {
                // the publisher exists
                publishers[topics[key]] += 1;
              }
              else {
                // the publisher doesn't exist
                publishers[topics[key]] = 1;
              }
           } 
            });
        }
    
        // gets the observers, calculates sum, caches their reference
        var _manageObservers = function() {
            $('[data-observe]').each(function(){
                var el = $(this);
           // create random data-name
           el.attr('data-name', _makeRandomString());
                var datas = el.data('observe').toString().split(' '); // make an array again if we have multiple attachments
                observers[el.data('name')] = (function(){
                    var sum = (function(){
                        var sum2 = 0;
                // if datas[key] is found in publishers array, add it to sum
                        for( var key in datas ) {
                   var temp = publishers[datas[key]];
                           if( temp ) {
                      sum2 += temp;
                 }
                        }
                        return sum2;
                    })();
                    var reference = el; // caching, so it is faster !
                    var topicsObserved = datas; 
                    // we need this when a user clicks data-publish, we need to see which DOM obj. are observing this.
    
                    // i really like revealing module pattern...i got used to it
                    return {
                        sum: sum,
                        reference: reference,
                topicsObserved: topicsObserved
                    }
          })();
            })
        }
    
        var init = function() {
        _managePublishers();
        _manageObservers();
            $('[data-publish]').on( 'click', function(){
          observer.publish( $(this).data('publish'), $(this) );
        });
            $('select').on('change', function(){
           var cache = $(this);
          // if in this select there is an option which has value 1(there is chance that it triggered a succesfull publish) we publish that too
           observer.publish( cache.find('[data-value="1"]').data('publish'),  cache.find('[data-value="1"]') );
                var el = cache.find('[data-publish]:selected');
           observer.publish( el.data('publish'), el );
            })
        }
    
    
        return {
            init: init,
            // in case you want to add after page load
            // warning: i didn't test these methods. maybe it can be a bug somewhere
            // it is not in my scope to test these, as i won't need for this example any adding after page load
            publish: observer.publish,
            subscribe: observer.subscribe
        }
    })();
    
    ObserverPlugin.init();
    

    是的,就是这样。你可以再次看到我的 codepen (here) 我以插件的形式制作了它。您可以将多个发布者附加到观察者,可以将具有相同值的多个发布者附加到一个观察者,或者您可以仅将一个发布者附加到一个观察者。我试图让它尽可能高效。我希望这可以帮助你 Jashwant :)

    (已编辑。现在它支持数据发布中的多个字符串,玩得开心!) (已编辑。您不必向 HTML 观察者添加随机数据名称字符串)

    【讨论】:

    • 非常有用和出色的答案 - 我会说几乎像教科书一样:-)
    【解决方案2】:

    我喜欢您基于少量控件对元素可见性进行动态处理的想法。我尝试制作一些,目前您可以根据单选按钮设置其他元素的可见性并选择项目。下面的解决方案假设控制其他元素可见性的元素具有id-attribute。也许,出于性能原因,您可以将其更改为新属性,例如:data-has-dependents,因此id-attribute 仅用于其值。如果有很多带有 id 的元素,那么这可能是实用的。

    如果选择了两个不同的值,我还允许一些元素可见。 vehicle 具有以下值 23 的逗号分隔列表:

    <p data-dependent='{"vehicle": "2,3" }'>Rocket engine type ? Gas turbine
      <input type="radio" value="gasturbine" name="enginetype" />Aerojet Rocketdyne
      <input type="radio" value="aerojet" name="enginetype" />
    </p>
    

    所以如果用户选择飞机或航天飞机,那么上面的单选按钮是可见的。

    如果选择车载冰淇淋卡车或沙漠类型冰淇淋,则冰淇淋类型字段也可见:

       <p data-dependent='{"vehicle": "4", "dessert": "icecream" }'>Type of Ice Cream? Vanilla:
          <input type="radio" value="vanilla" name="icecream" />Strawberry Ice cream:
          <input type="radio" value="strawberry" name="icecream" />Coffee Ice cream:
           <input type="radio" value="coffee" name="icecream" />
        </p>
    

    更新,在下面的代码中,checkDependencies 函数在以下情况之一发生后使用:

    1. 页面已加载
    2. 选择具有 id 的元素已更改
    3. 单选按钮被点击,单选按钮的父级有一个id

    checkDependencies函数中,第一个循环遍历每个依赖的数据元素,得到依赖于数据的元素的属性值。在第二个循环中,获取每个具有 id 的元素的值。最后,在第三和第四个循环中,先前找到的相关数据元素值用于查找匹配的选择 (2.) 或单选按钮的父元素 (3.)。更准确地说,第三个循环用于允许依赖数据元素使用多个键和值,例如:data-dependent='{"vehicle": "4", "dessert": "icecream" }'。例如,第四个循环允许从属元素为一个键具有两个值。 data-dependent='{"vehicle": "2,3" }' 。因此,第三和第四个循环是为了灵活。

    我认为可能有比这更复杂的答案 + 我认为像 AngularJS 这样的基于 MVC 的 JavaScript 框架在这种情况下可能非常实用。

    checkDependencies();
    
    $("select[id]").on('change', function() {
    
      checkDependencies();
    });
    
    $("[id] > :radio").on('click', function() {
    
      checkDependencies();
    });
    
    function checkDependencies() {
    
      $("[data-dependent]").each(function() {
    
        $dependent = $(this);
    
        var data = $(this).data("dependent");
    
        var keyCount = Object.keys(data).length;
    
        var checkedCount = 0;
    
        var setVisible = false;
    
        var dependentValues = $.map(data, function(value, index) {
          return value;
        });
    
    
        $("[id]").each(function() {
    
          var hasRadioButtons = $(this).find(":radio").length;
    
          var elementId = $(this).attr("id");
    
          var elementValue;
    
          if (hasRadioButtons) {
    
            elementValue = $(this).find(":checked").val()
    
          } else {
    
            elementValue = $(this).val();
          }
    
          for (i = 0; i < keyCount; i++) {
    
            var dependentId = Object.keys(data)[i];
    
            //if multiple values for one key
            var values = dependentValues[i].split(",");
    
            for (j = 0; j < values.length; j++) {
              var dependentValue = values[j];
    
              if (elementId === dependentId) {
    
                //check if value selected
                if (elementValue === dependentValue) {
    
                  checkedCount += 1;
    
                  setVisible = true;
    
                  $dependent.show();
    
                  //found element, exit inner loop
                  break;
    
                } else {
    
                  //hide if not previously set visible
                  if (!setVisible)
                    $dependent.hide();
    
                  //if all element dependencies found exit inner loop
                  if (keyCount === checkedCount)
                    break;
    
                }
              }
    
            }
          }
    
        });
    
      });
    
    }
    [data-dependent] {
      display: none;
    }
    #dessert {
      margin-left: 20px;
      display: inline
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
    <select id="vehicle">
      <option value="0">Bike</option>
      <option value="1">Car</option>
      <option value="2">Plane</option>
      <option value="3">Space shuttle</option>
      <option value="4">Ice Cream Truck</option>
    </select>
    
    <p id="dessert">Dessert: Sweets
      <input type="radio" value="sweets" name="dessert" />Ice cream
      <input type="radio" value="icecream" name="dessert" />Cake
      <input type="radio" value="cake" name="dessert" />
    </p>
    
    
    <p data-dependent='{"vehicle": "0" }'>Bike brand ? Trek
      <input type="radio" value="trek" name="bike" />Giant
      <input type="radio" value="gt" name="bike" />Cannondale
      <input type="radio" value="cannondale" name="bike" />
    
    </p>
    
    <p data-dependent='{"vehicle": "1" }'>Car's fuel type ? Petrol
      <input type="radio" value="petrol" name="fueltype" />Diesel
      <input type="radio" value="diesel" name="fueltype" />Biodiesel
      <input type="radio" value="biodiesel" name="fueltype" />Hybrid
      <input type="radio" value="hybrid" name="fueltype" />
    </p>
    
    <p data-dependent='{"vehicle": "2,3" }'>Rocket engine type ? Gas turbine
      <input type="radio" value="gasturbine" name="enginetype" />Aerojet Rocketdyne
      <input type="radio" value="aerojet" name="enginetype" />
    </p>
    
    <p data-dependent='{"vehicle": "1" }'>Left / right hand drive? Left:
      <input type="radio" value="left" name="dir" />Right:
      <input type="radio" value="right" name="dir" />
    </p>
    
    
    <select data-dependent='{"dessert": "sweets" }'>
      <option value="0">Jelly beans</option>
      <option value="1">Haribo gummy bears</option>
      <option value="2">Fruit candy</option>
    </select>
    
    <p data-dependent='{"vehicle": "4", "dessert": "icecream" }'>Type of Ice Cream? Vanilla:
      <input type="radio" value="vanilla" name="icecream" />Strawberry Ice cream:
      <input type="radio" value="strawberry" name="icecream" />Coffee Ice cream:
       <input type="radio" value="coffee" name="icecream" />
    </p>
    
    
    <p data-dependent='{"dessert": "cake" }'>Type of cake? Chocolate Cake:
      <input type="radio" value="chokocake" name="cake" />Cheesecake:
      <input type="radio" value="cheesecake" name="cake" />Carrot Cake:
       <input type="radio" value="carrotcake" name="cake" />
    </p>

    【讨论】:

    • 看起来不错 :) 但是,我并没有完全理解逻辑。我明天再检查一下。
    • @Jashwant 嗨,很高兴你喜欢它 :-) 你说得对,我没有提供足够的细节来回答我的问题。现在我已经用一些关于如何编码工作的逻辑更新了我的答案 - 我希望它有所帮助。如果您仍有疑问,请告诉我,我会尝试更深入地了解代码的工作原理。
    【解决方案3】:

    $('select').change(function () {
        if($(this).val() === '1') {
            $('p').show();
        }
        else {
            $('p').hide();
        }
    });
    p {
        display: none;
    }
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
    <select>
        <option value="0">Bike</option>
        <option value="1">Car</option>
    </select>
    
    <p>Left / right hand drive ?
        Left: <input type="radio" value="left" name="dir" />
        Right: <input type="radio" value="right" name="dir" />
    </p>

    【讨论】:

      【解决方案4】:

      改变你可以使用的样式

      $('p').css("display","");

      $('p').css("display","none");

      【讨论】:

      • 问题不要求hide字段的另一种方式。
      猜你喜欢
      • 2018-07-16
      • 1970-01-01
      • 2013-08-02
      • 2012-06-15
      • 1970-01-01
      • 2023-03-22
      • 1970-01-01
      • 2016-12-22
      • 1970-01-01
      相关资源
      最近更新 更多