title: Show/Hide elements by dependency
body: <p>I was inspired by this topic <a href="https://stackoverflow.com/questions/28165131/show-hide-form-fields-based-on-value-of-other-fields">Show/Hide form fields based on value of other fields</a> and made some changes to working it  not only on select.
  Here is example:</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>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 = {},
          publishers = [];
  
      var _setStylesheet = (function() {
          // Create the &lt;style&gt; tag
          var style = document.createElement("style");
          // Add a media (and/or media query) here if you'd like!
          // style.setAttribute("media", "screen")
          // style.setAttribute("media", "only screen and (max-width : 1024px)")
  
          // WebKit hack :(
          style.appendChild(document.createTextNode(""));
          // Add the &lt;style&gt; element to the page
          document.head.appendChild(style);
          return style.sheet;
      })();
  
      // observer pattern &amp; 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 &lt; 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) &gt; -1;
      };
  
      // topic is the topic
      // reference is a reference to the DOM object clicked
      var _observerFunction = function(topic, reference) {
          var number = reference.attr('data-publish-value');
          var topics = topic.toString().split(' ');
          var length = topics.length;
          //var display;
          for( var key in observers ) {
              for( var i = 0; i &lt; 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 === 1 ) {
                  // it is 0, so show that goddam DOM obj ! :))
                  // again, put here 'var' for clarity
                  // does not affect the code
                  //display = 'block';
                  _changeProperty(key,observers[key].property,1);
              }
              else {
                  // it is not 0, so hide it
                  //display = 'none';
                  _changeProperty(key,observers[key].property,0);
              }
              //observers[key].reference.css('display', display);
          }
          // change value to -1 or 1
          if( number === '-1' ) {
              reference.attr('data-publish-value', '1');
          }
          else {
              reference.attr('data-publish-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-publish-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-observe-name', _makeRandomString());
              var datas = el.data('observe').toString().split(' '); // make an array again if we have multiple attachments
              observers[el.data('observe-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, topicsObserved = datas; // caching, so it is faster !
  
                  // 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,
                      property: $(reference).data('observe-property') //style-display[none/block],attr-disable/-,class-active/inactive
                  }
              })();
          })
      };
  
      var _changeProperty = function(observer, property, status) {
          if(property === "style") {
              if(status === 1) {
                  observers[observer].reference.css('display', 'block');
              }
              else {
                  observers[observer].reference.css('display', 'none');
              }
          }
          else if(property === "attr") {
              if(status === 1) {
                  $(observers[observer].reference).removeAttr('disabled');
              }
              else {
                  $(observers[observer].reference).attr('disabled','disabled');
              }
          }
          else if(property === "class") {
              if(status === 1) {
                  $(observers[observer].reference).removeClass('inactive');
              }
              else {
                  $(observers[observer].reference).addClass('inactive');
              }
          }
      };
  
      var init = function() {
          _managePublishers();
          _manageObservers();
          $('[data-publish]:not(select)').on( 'click', function(){
              observer.publish( $(this).data('publish'), $(this) );
          });
          $('select[data-publish]').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(':selected');
              observer.publish( el.data('publish'), el );
          });
          $('[data-publish]').each( function() {
              if(this.type !== 'radio' || this.type !== 'checkbox' || this.nodeName !== 'SELECT') {
                  observer.publish( $(this).data('publish'), $(this) );
              }
          });
  
          // when observers[xx].sum is 0 it must be activated always, otherwise it is always invisible
          $.each( observers, function( key, value ) {
              if(value.topicsObserved) {
                  $.each( value.topicsObserved, function( key2, value2 ) {
                      if(!publishers.hasOwnProperty(value2)) {
                          $(value.reference).css('display', 'block');
                          return;
                      }
                      if(value.property === "style") {
                          _setStylesheet.insertRule('[data-observe-name="'+key+'"] {display: none;}', _setStylesheet.rules.length);
                      }
                      else if(value.property === "attr") {
                          $(value.reference).attr('disabled','disabled');
                          _setStylesheet.insertRule('[disabled] {cursor: not-allowed;}', _setStylesheet.rules.length);
                      }
                      else if(value.property === "class") {
                          $(value.reference).addClass('inactive');
                      }
                  });
              }
          });
      };
  
      return {
          init: init,
          publish: observer.publish,
          subscribe: observer.subscribe
      }
  })();
  ObserverPlugin.init();</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"&gt;&lt;/script&gt;
  &lt;form method="post" name="form"&gt;
      &lt;input type="text" name="text" value=""&gt;
      &lt;div class="wraper"&gt;
          &lt;label&gt;&lt;input type="radio" name="typ" value="subscribe" checked data-publish="newslist" &gt;Subscribe&lt;/label&gt;
          &lt;label&gt;&lt;input type="radio" name="typ" value="unsubscribe" data-publish="unsubscribe" &gt;Unsubscribe&lt;/label&gt;
      &lt;/div&gt;
      &lt;div class="wraper"&gt;
          &lt;button type="submit" name="subscription" value="1" data-observe="newslist" data-observe-property="attr" &gt;
              &lt;span&gt;Send&lt;/span&gt;
          &lt;/button&gt;
      &lt;/div&gt;
      &lt;div data-observe="unsubscribe" data-observe-property="style"&gt;
          &lt;label&gt;
              &lt;input type="checkbox" name="confirm" value="1" data-publish="newslist"&gt;
              &lt;span&gt;Confirm&lt;/span&gt;
          &lt;/label&gt;
      &lt;/div&gt;
  &lt;/form&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>jsfiddler also here <a href="https://jsfiddle.net/ogxusLja/" rel="nofollow noreferrer">https://jsfiddle.net/ogxusLja/</a></p>
  
  <ol>
  <li>Problem is that <code>radio inputs</code> fired click event always, not only on
  change to second <code>radio input</code>. </li>
  <li>Another problem is that <code>Confirm</code>
      checkbox is hidden on load.</li>
  </ol>
  
  <p>Expected behavior:</p>
  
  <ol>
  <li>Selected <code>Unsubscribe radio</code> will enable the <code>Send</code> button and hide the <code>Confirm checkbox</code>.</li>
  <li>Selected <code>Subscribe radio</code> and <code>Confirm checkbox</code> will together enable the <code>Send</code> button.</li>
  <li>Selected <code>Subscribe radio</code> or 'Confirm checkbox' (if only one is selected) <code>Send</code> button will be disabled.</li>
  </ol>