JQuery – Traversing the DOM

I was writing a little script to make my Click to Call product talk nicely with JQuery and encountered many problems – so I thought I would put some of the problems and my solutions to them.

Firstly, the goal was to create a ‘dynamic’ version of the click to call service – which previously has been hardcoded using element ID’s with remote javascript.
So the first goal was removing all that and using classes and relying on the DOM to make sure I had the correct elements selected.

The Click to Call system uses a timer with a curl request to the switch to monitor progress of the call – so I needed to make sure I had state and kept track of which ‘click to call’ form was being used.

The first step was to make a button to create the click to call form dynamically. This was done using:

$(document).ready(function() { 
  $('#add_c2c_btn').click( function(){
    // copy the click to call div
    $('.container').append($('.c2call').first().clone());
    elem = $('.c2call').last();
    elem.show(); // show it

with the HTML:

Dynamic JQuery Click To Call Examples

test

Just to explain, the above HTML has a hidden div, c2call. When the button is pressed, JQuery uses the append() function on the ‘container’ div, with a clone() of the c2call div. It then finds the last() class ‘c2call’ div (which is our new one, as it was appended to the ‘container’ div at the bottom) and runs the show() function to make it all visible.

Next we need to setup some listeners on our input to validate input, and then to process the click of the clicktocall button.

// traverse the dom and find the input num element related to this 'c2call'
    num = elem.children().last().children().next().children();
    // put a onclick on the button next to our number input.
    num.parent().next().children().click( function (){
      // process the call
      var c2call_pageUrl = window.location.href;
      var c2call_numtocall = $(this).parent().parent().children().next().children().val(); // traverse the dom to get the relevant num input
      var c2call_savenum = 'n';
    	var c2call_callStart = new Date()
    	var c2call_url_path = '/click2call/dial_go.php';
    	var params = 'test=' + c2call_testrun + '&url_base=' + c2call_url_base + '&url_path=' + c2call_url_path + '&date=' + Date() + '&email=' + c2call_email + '&sp_email=' + c2call_sp_email + '&csnum=' + c2call_numtocall + '&callStart=' + c2call_callStart.getTime() + '&pageUrl=' + c2call_pageUrl + '&csid=' + script_id;
    	c2call_http3.open("POST", '/c2call/c2call.php', false);
    	c2call_http3.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    	c2call_http3.setRequestHeader("Content-length", params.length);
    	c2call_http3.setRequestHeader("Connection", "close");
    	c2call_http3.onreadystatechange = c2call_callResponse;
    	c2call_http3.send(params);
      
    	// remember which button was pressed - used to display status updates/errors further down.
      c2call_which_btn = $(this);
    	
    	c2call_which_btn.attr("disabled",true);
      c2call_which_btn.val("Initiating...");
      // You could do further notificaiton here if required.
      // eg.
    });
    
    num.keydown(function(e) {
      var key = e.charCode || e.keyCode || 0;
      // allow backspace, tab, delete, arrows, numbers and keypad numbers ONLY
      return (
        key == 8 || 
        key == 9 ||
        key == 46 ||
        (key >= 37 && key <= 40) ||
        (key >= 48 && key <= 57) ||
        (key >= 96 && key <= 105)
      );
    });

    num.keyup(function(e) {
      var key = e.charCode || e.keyCode || 0;

      // make sure area code is included
      if($(this).val().length >= 1){
        if($(this).val().substring(0,1) != '0'){
          $(this).parent().nextAll('.c2call_notify:first').html("Please include the Area Code.");
          // reset the value field to prevent them continuing
          $(this).val('');
        }
      }
      
      // check if mobile allowed
      //alert(c2call_bar_mobile);
      if(c2call_bar_mobile == 'y'){
        // no '04' allowed
        if($(this).val().substring(0,2) == '04'){
          $(this).parent().nextAll('.c2call_notify:first').html("Sorry Mobiles not Allowed...");
          // reset the value field to prevent them continuing
          $(this).val('0');
        }
      }
      
      if(c2call_bar_int == 'y'){
        // everything must be 0{24378}
        //alert($(this).val().substring(0,2));
        // TODO if required?
      }
      
      if($(this).val().length == 10){        
        // enable the button (find the correct button using DOM traversal)
        $(this).parent().next().children().attr("disabled",false);
        $(this).parent().next().children().val("Ready To Call");
      }else{
        $(this).parent().next().children().attr("disabled",true);
        $(this).parent().next().children().val("Waiting...");
      }
    });
  }); // end of add button .click
}); // end of the document.ready

Ok, now I know there was a lot of code so lets work through it all. I use the children() function to go ‘down’ the dom, in this case it was on elem – which was our c2call div around the outside. Then I grab the last() dom element, which is the div class ‘thinkcont’. Inside that div with children() again, and finally the next() element in the DOM to give us the input with the class of ‘c2call_numtocall’.

Then I setup a click() function using the button, which I get to using: parent() to select the ‘thright1′ div, then next() to move to the next div, ‘butt2′ then get its children() which is the input ‘c2call_btn’. This then does the click to call functionality to send the curl request to the voice switch.

The next bit of interesting code is the keydown and keyup functions. The keydown function ensures only numeric data is inputed. The keyup is used for validation to ensure area codes, and other baring is honored by the script. I also do a little DOM traversal here too:

$(this).parent().nextAll('.c2call_notify:first').html("Sorry Mobiles not Allowed...");
and
$(this).parent().next().children().attr("disabled",false);

Explanation: this element, which is the text input remember, the parent() which is ‘thright1′, then nextAll(‘.c2call_notify:first’) which finds the :first div on the same DOM level as we currently are (inside the ‘thinkcont’ div) with the class ‘c2call_notify’. It’s a little bit of magic I know – but I gave it a decent test and couldn’t break it so I happy with that.
The next line was this element, the input, parent() – ‘thright1′, next() which is ‘butt2′, then children() which is the input button.

Also you will notice so I can keep track of which click to call processed the call, I put the current button into the global javascript dom, c2call_which_btn.

I’ll put the finished version online once I polish it some more at: Click to Call Installation

Comments