4
\$\begingroup\$

There are buttons on a page, each corresponding to a different code that needs to be sent to PubNub DSN, which is published to my home server to operate specific on/off commands for various lights in home. This is primarily a learning experience for now.

I currently have 4 basic buttons on page:

<section id="main" role="main">

        <button type="button" name="d_on">Dining Room On</button>

        <button type="button" name="d_off">Dining Room Off</button>

        <button type="button" name="a_on">Bedroom On</button>

        <button type="button" name="a_off">Bedroom Off</button>

</section>

NOTE: "Name is the code that needs to be sent, depending on which button is clicked, corresponding to the specific device"

Here is all the JavaScript I built to make it work. This was pretty easy and works perfectly:

<script src="https://cdn.pubnub.com/pubnub-3.15.2.min.js"></script>
<script>

(function(){

    //DOM                       

    var button1 = document.querySelector('button[name=d_on]');
    var button2 = document.querySelector('button[name=d_off]');
    var button3 = document.querySelector('button[name=a_on]');
    var button4 = document.querySelector('button[name=a_off]');
    var code1 = button1.name;
    var code2 = button2.name;
    var code3 = button3.name;
    var code4 = button4.name;

    // PubNub Channel Name
    var channel = 'demo';

    //Init - Sub and Pub key
    var p = PUBNUB.init({
        subscribe_key: 'sub-c...',
        publish_key: 'pub-c...'
    });

    // Sending data
    function jetson1(){
        p.publish({
            channel : channel,  
            message : {"text":"From Website",
                       "code": button1.name}
        });
    }

    function jetson2(){
        p.publish({
            channel : channel,
            message : {"text":"From Website",
                       "code": button2.name}
        });
    }

    function jetson3(){
        p.publish({
            channel : channel,
            message : {"text":"From Website",
                       "code": button3.name}
        });
    }

    function jetson4(){
        p.publish({
            channel : channel,
            message : {"text":"From Website",
                       "code": button4.name}
        });
    }

    //Click event
    button1.addEventListener('click',jetson1);
    button2.addEventListener('click',jetson2);
    button3.addEventListener('click',jetson3);
    button4.addEventListener('click',jetson4);

    })();       
</script>

Problem is, while this works, it is very wordy and is only for 2 devices. I plan on adding many more and need a way to essentially only add the button and name attribute. The function then dynamically sends what it needs to.

I have worked on different things for hours and hours trying to get my head around the logic and syntax and can't seem to figure it out.

For example, I used the following and can get an array of the codes, but am stuck on what I could even do with this and think I may be making it more complicated than necessary. I appreciate any nudges to get my brain thinking in the right direction.

var buttons = document.querySelectorAll('button');

var code = new createCodes();

function createCodes(){
    var code = []

    for (var i = 0, len = buttons.length; i < len; i++) {
    code[i] = buttons[i].name;
    }

    return code;
    }       

Researching a bit further, I tried to build the click events like so, but the problem is that it doesn't wait for the actual button click, they all fire at page load and then after that, the buttons do not respond to a click any longer.

    (function(){

        // PubNub Channel Name
        var channel = 'demo';

        //Init - Sub and Pub key
        var p = PUBNUB.init({
                subscribe_key: 'sub-c-',
                publish_key: 'pub-c'
        });

        // Sending data
        function jetson(code){
           p.publish({
           channel : channel,   
           message : {"text":"From Website",
                      "code": code}
            });
        }

        var $ = function (selector) {
            return document.querySelector(selector);
        };

        var buttons = $('#main').getElementsByTagName('button');

        for (var i = 0; i < buttons.length; i++) {
           var button = buttons[i];
           var code = button.name;
           button.onclick = jetson(code);
        }           
        })();
</script>
\$\endgroup\$
2
\$\begingroup\$

You're on the right track, but you need to use jquery more effectively. I'm sorry, I thought you were already using jQuery. I see now that you have your own little homegrown jQuery-like function. You should just use jQuery instead, and that'll make this a lot easier.

Once you've added jQuery, then just by adding a class to the buttons you want this to happen on, and then binding an onclick event handler to that class you can remove all of your code duplication. As a note, you might not be able to run the stack snippet - I was getting some errors about XSS issues, but I have a weird browser configuration so I don't know if it'll affect you.

(function(){
    var pubnubChannelName = 'demo'
    var p = PUBNUB.init({
        subscribe_key: 'sub-c...',
        publish_key: 'pub-c...'
    });

    function publishData(code) {
        p.publish({
            channel: pubnubChannelName,
            message: {
              "text": "From Website",
              "code": code
            }
        });     
    }
  
    $(document).ready(function() {
      $('.pubnub_button').click(function() {
        publishData($(this).attr('name'));
      });  
    });
})();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdn.pubnub.com/pubnub-3.15.2.min.js"></script>
<section id="main" role="main">
    <button type="button" class='pubnub_button' name="d_on">Dining Room On</button>
    <button type="button" class='pubnub_button' name="d_off">Dining Room Off</button>
    <button type="button" class='pubnub_button' name="a_on">Bedroom On</button>
    <button type="button" class='pubnub_button' name="a_off">Bedroom Off</button>
</section>

If you're going to continue to add more of these, however, you're probably going to want some dynamically generated HTML. The specifics of that are beyond the scope of this question, but I encourage you to look into those solutions as this project expands.


At times it is also desirable to not use any external libraries if possible, and I'm a firm believer that knowing how to do something in pure JavaScript (not just jQuery) is important. I also think that in many cases, adding jQuery as a dependency is unnecessary. Here you can easily accomplish the same thing in an almost identical fashion without adding jQuery. You'll notice that I actually had to change very little about your code - I just assigned the onclick event to an actual function, and I cleaned up a bit towards the end.

(function() {
  var channel = 'demo';

  var p = PUBNUB.init({
    subscribe_key: 'sub-c-',
    publish_key: 'pub-c'
  });

  function jetson(code) {
    p.publish({
      channel: channel,
      message: {
        "text": "From Website",
        "code": code
      }
    });
  }

  var $ = function(selector) {
    return document.querySelectorAll(selector);
  };

  var buttons = $('#main > button');

  for (var button in buttons) {
    button.onclick = function() {
      jetson(button.getAttribute('name'));
    };
  }
})();
\$\endgroup\$
  • \$\begingroup\$ This was exactly what I was looking for! I knew it could be done as easy as this, but it is sometimes hard to put your finger on it and know what direction to take when you are still new. As the internet grows, and information is abundant, its easy to get lost in some fancy solutions. You are right, it wasn't jQuery I was using, was actually using some samples from a tutorial on the subject and that is what he did. Thought it was weird, but went with it. On another note, I am curious as to why what I had at the end of my initial post caused my click events to fire immediately? \$\endgroup\$ – shelzmike Jul 16 '16 at 22:28
  • 1
    \$\begingroup\$ @shelzmike The line button.onclick = jetson(code); is assigning button.onclick to the return value of jetson(code). You probably wanted something like button.onclick = function() { return jetson(code); };. You assign it an anonymous function that closes over the current values of jetson and code. \$\endgroup\$ – Dannnno Jul 16 '16 at 23:04
  • 1
    \$\begingroup\$ @shelzmike to answer your question, it's because of this line button.onclick = jetson(code); that will NOT make the click event fire jetson(code), instead the function is invoked and the result (which is undefined) is assigned to the click handler. Hence why everything gets executed on page load. One way is avoid this is to wrap it in a function at the point of assigning, e.g., button.onclick = function() { jetson(code); }; - that way clicking the button invokes the anonymous function which itself has the context needed. \$\endgroup\$ – VLAZ Jul 16 '16 at 23:05
  • \$\begingroup\$ That makes sense now. It has been so long since I worked with Javascript, I forgot all about that. (If I recall, I had a problem very similar back when). Thanks to both for the clarification. \$\endgroup\$ – shelzmike Jul 18 '16 at 4:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.