Implement a UI Dialpad for the JavaScript Programmable Voice Quickstart

Developers strategizing over UI dialpad implementation on a board
September 28, 2022
Written by
Reviewed by
Paul Kamp
Twilion

Ahoy there! Have you explored the Twilio Voice SDK JavaScript Quickstart? Are you now ready to learn how to use your SDK-powered app to interact with an Interactive Voice Response (IVR)? That is exactly what this blog post will cover – and by the end of it, you should be able to dial a number using your cursor, or interact with an automated IVR you have setup.

Solution: a UI dialpad for the Twilio JS Voice Quickstart

Our Quickstart does not contain a UI dialpad out of the box, nor does our SDK. The purpose of our Quickstart app is to familiarize yourself with placing and receiving calls via your web browser, and to give you a taste of what you can achieve using our SDK. Although the Quickstart wasn’t built to be a full production application, I will run through how you can change the UI to include a dialpad for further testing.

The main resource I will be using for the front end will be Azizur Rahman's repository on CodePen which shows off the source code needed to produce a modern looking dialpad. I will discuss how we can take advantage of this repository and include it in our quickstart application with some editing.

Prerequisites


If you have not already done this, please feel free to take a look at our blog post running over the deployment of your application.

HTML 

Inside the code editor of your choice, navigate to your file explorer (usually the left margin), and open up our main HTML file for our application. The location of this file can be found in your public folder here: voice-javascript-sdk-quickstart-node/public/index.html

Public folder Structure

Once open, at the very beginning of the file you will see the “header” section denoted by the <head>  keyword. This is where we will declare the stylesheets and fonts we are going to use within our dialpad. All we need to do is include the following code within the header section of your application, just below <link rel="stylesheet" href="site.css" />:

<!DOCTYPE html>
<html>
  <head>
    <title>Twilio Voice JavaScript SDK Quickstart</title>
    <link rel="stylesheet" href="site.css" />
    <!-- Stylesheets for dialpad-->
    <link href="https://fonts.googleapis.com/css?family=Exo" rel="stylesheet">
    <link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css" rel="stylesheet">
  </head>

Once we have done that, we need to include our dialpad as a whole. We are going to do this by changing the existing HTML document to include our various dialpad elements such as buttons to represent each number 0-9, letters of the alphabet on each of those numbers, and various icons to represent hanging up a call.

Regarding the layout of our HTML document, imagine a webpage broken up into 3 vertical columns or thirds. Each one of these will be used to house different UI elements. For our diapad, we'll include the dialpad in the center column.

If you scroll down a little in your HTML document, you should see a section exactly the same as the following:

<section class="center-column">

Navigate to line 25. Under this section is where we will include the code for our dialpad. 

You will see some code in its place which will need to be replaced. This originally was used to capture the numbers, but as we are using a dialpad now, we will want to remove this. Replace all the code from the “center-column” class with the following snippet:

<section class="center-column">
  <h2 class="instructions">Make a Call</h2>
  <div id="call-controls" class="hide">
    <div class="container">
      <form name="input">
        <input id="phone-number" type="text" />
        <div class="row">
          <div class="digit" id="one">1</div>
          <div class="digit" id="two">2 <div class="sub">ABC</div>
          </div>
          <div class="digit" id="three">3 <div class="sub">DEF</div>
          </div>
        </div>
        <div class="row">
          <div class="digit" id="four">4 <div class="sub">GHI</div>
          </div>
          <div class="digit" id="five">5 <div class="sub">JKL</div>
          </div>
          <div class="digit">6 <div class="sub">MNO</div>
          </div>
        </div>
        <div class="row">
          <div class="digit">7 <div class="sub">PQRS</div>
          </div>
          <div class="digit">8 <div class="sub">TUV</div>
          </div>
          <div class="digit">9 <div class="sub">WXYZ</div>
          </div>
        </div>
        <div class="row">
          <div class="digit">* </div>
          <div class="digit">0 </div>
          <div class="digit"># </div>
        </div>
        <div class="botrow">
          <div class="digit">+ </div>
          <div id="button-call" type=submit>
            <i class="fa fa-phone"></i>
          </div>
          <div class="hide" id="hangup" type=submit>
            <i id="hangup-icon" class="fa fa-phone"></i>
          </div>
          <div class="hide" id="button-hangup-incoming" type=submit>
            <i id="hangup-icon" class="fa fa-phone"></i>
          </div>
          <i class="fa fa-long-arrow-left dig" id="backspace" type=submit></i>
        </div>
      </form>
      <div class="row">
        <input class="setting hide" type="button" value="Add Participant" id="add-participant">
      </div>
      <div class="row">
        <input class="setting hide" type="button" value="Start Stream" id="button-stream-start">
        <input class="setting hide" type="button" value="Stop Stream" id="button-stream-stop">
      </div>
      <div id="incoming-call" class="hide">
        <br>
        <h2>Incoming Call Controls</h2>
        <p class="instructions"> Incoming Call from <span id="incoming-number"></span>
        </p>
        <div class="row">
          <input class="setting" type="button" value="Answer" id="button-accept-incoming">
          <input class="setting" type="button" value="Reject" id="button-reject-incoming">
        </div>
      </div>
      <div id="volume-indicators" class="hide">
        <label>Mic Volume</label>
        <div id="input-volume"></div>
        <br />
        <br />
        <label>Speaker Volume</label>
        <div id="output-volume"></div>
      </div>
    </div>
  </div>

Once added to your HTML document, the full file should look the same as the following found on Github.

CSS

Open up the CSS file in our quickstart called site.css found at the following location:  voice-javascript-sdk-quickstart-node/public/site.css

Once open, navigate your way down to the bottom of the file. You will see a section with the following comment: /* Other Styles */ Just above this, we are going to add in the styles for the hangup button with the following snippet of code:

#button-hangup-incoming{
     display: inline-block;
     background-color: #ec2f0e;
     padding: 4px 30px;
     margin: 10px;
     color: white;
     border-radius: 4px;
     cursor: pointer;
}

Your CSS file should now look just like this:

#button-hangup-incoming{
     display: inline-block;
     background-color: #ec2f0e;
     padding: 4px 30px;
     margin: 10px;
     color: white;
     border-radius: 4px;
     cursor: pointer;
}
/* Other Styles */
 .hide {
     position: absolute !important;
     top: -9999px !important;
     left: -9999px !important;
}
 button:disabled {
     cursor: not-allowed;
}

Now, navigate to the very last line of your CSS file and hit enter. Proceed to then copy the following snippet and paste it into your CSS file at the very bottom. The snippet in question will look like this:

.row {
     margin: 0 auto;
     width: 280px;
     clear: both;
     text-align: center;
     font-family: 'Exo';
}
 .digit, .dig {
     float: left;
     padding: 10px 30px;
     width: 30px;
     font-size: 2rem;
     cursor: pointer;
}
 .sub {
     font-size: 0.8rem;
     color: grey;
}
 .container {
     background-color: white;
     width: 320px;
     padding: 20px;
     margin: 30px auto;
     height: 420px;
     text-align: center;
     box-shadow: 0 14px 28px rgba(0, 0, 0, 0.25), 0 10px 10px rgba(0, 0, 0, 0.22);
}
 #button-call {
     display: inline-block;
     background-color: #66bb6a;
     padding: 4px 30px;
     margin: 10px;
     color: white;
     border-radius: 4px;
     float: left;
     cursor: pointer;
}
 #hangup {
     display: inline-block;
     background-color: #ec2f0e;
     padding: 4px 30px;
     margin: 10px;
     color: white;
     border-radius: 4px;
     float: left;
     cursor: pointer;
}
 .botrow {
     margin: 0 auto;
     width: 280px;
     clear: both;
     text-align: center;
     font-family: 'Exo';
}
 .digit:active, .dig:active {
     background-color: #e9e9e9;
}
 #button-call:hover {
     background-color: #81c784;
}
 #hangup:hover {
     background-color: #ec6c55;
}
.dig {
     float: left;
     padding: 10px 20px;
     margin: 10px;
     width: 30px;
     cursor: pointer;
}

Once you have done that, your complete CSS file should now look like the complete source code found on Github.

Javascript/JS

To tie everything done so far together, we will need to provide our application with some logic to handle the dialpad inputs. This is where we will make use of JavaScript to interpret user interaction with the dialpad (such as button clicks).  

To get started you will want to open up your JavaScript file within your quickstart application. At the beginning of file you will see the line:

const outgoingCallHangupButton = document.getElementById("button-hangup-outgoing");

Change this to the following to make it match our HTML element:

const outgoingCallHangupButton = document.getElementById("hangup");

Then scroll down the page a little more until you see where we declare our device and token variables:

let device;
let token;

Below these, you will see some space in the file. We will need to add one more variable here which we will then use as part of our dialpad. These will represent our call and call status for our application where we will set it to false to begin. You should have the following after adding the variable to your JS file:

let device;
let token;
let callStarted=false;

Directly under this section in your JS file, you should see the following comment:

//Event Listeners

Underneath this is where we will add any listeners to our HTML elements, and in this case, as we are going to be utilizing a dialpad, we will need to place some onClick listeners underneath.
Currently our application only has one listener in place for outgoing calls, but we are soon going to change that. Navigate to where you see the following snippet underneath our Event Listeners comment. The code should look similar to this:

callButton.onclick = (e) => {
  e.preventDefault();
  makeOutgoingCall();
};

Now from the callButton.onclick = (e) down, highlight everything and paste in the following snippet. This will be our entire JS snippet  for our dialpad, which allows it to actually function with user interaction:

  e.preventDefault();
  callButton.classList.add("hide");
  outgoingCallHangupButton.classList.remove("hide");
  makeOutgoingCall();
  console.log(e);
};
// Digits button logic for dialpad
$(".digit").on('click', function() {
  var input = document.getElementById("phone-number");
  var current = input.value;
  var num = ($(this).clone().children().remove().end().text());
  input.value = current + num.trim();
  console.log(callStarted);
  if (callStarted == true) {
    var dtmf = num.trim();
    dtmf = dtmf.toString();
    console.log(dtmf);
    call.sendDigits(dtmf);
  }
});
$('.fa-long-arrow-left').on('click', function() {
  var input = document.getElementById("phone-number");
  var current = input.value;
  var newVal = current.slice(0, -1);
  input.value = newVal;
});

Stop just before the line:

getAudioDevicesButton.onclick = getAudioDevices;

Tie the dialpad JavaScript together

Now that you have included the majority of our JS code, we need to add a few more lines to tie everything we just added together. Scroll down some more until you see a section with the comment heading:

// SETUP STEP 3:
// Instantiate a new Twilio.Device

At the bottom of this code block you should see the line

codecPreferences: ["opus", "pcmu"],

This is for our codecs used. Just underneath this, we will place the following snippet.

//allow incoming calls while busy
allowIncomingWhileBusy: true,

Then we need to scroll down to the following section:

function updateUIAcceptedOutgoingCall(call) {
  log("Call in progress ...");
  callButton.disabled = true;
  outgoingCallHangupButton.classList.remove("hide");
  volumeIndicators.classList.remove("hide");
  bindVolumeIndicators(call);
}

function updateUIDisconnectedOutgoingCall() {
  log("Call disconnected.");
  callButton.disabled = false;
  outgoingCallHangupButton.classList.add("hide");
  volumeIndicators.classList.add("hide");
}

In our first code block for outgoing calls, we need to include our call status variable so we can keep an eye on when a call is active/started. We are going to add the line:

callStarted=true;

In our next code block (which is used when the same outgoing call is disconnected or hung up), we will add our same callStarted variable but set this to false as the call is no longer in progress.

callStarted=false;

As a result, the two code blocks now should look like the following:

function updateUIAcceptedOutgoingCall(call) {
  log("Call in progress ...");
  callButton.disabled = true;
  outgoingCallHangupButton.classList.remove("hide");
  volumeIndicators.classList.remove("hide");
  bindVolumeIndicators(call);
  callStarted=true;
}

function updateUIDisconnectedOutgoingCall() {
  log("Call disconnected.");
  callButton.disabled = false;
  outgoingCallHangupButton.classList.add("hide");
  callButton.classList.remove("hide");
  volumeIndicators.classList.add("hide");
  callStarted=false;
}

Last but not least, scroll down your JS file a little more until you see the comment:

//ACCEPT INCOMING CALL

Under this you will see a code block and function used to handle or accept an incoming call. When the call is accepted, our call is then seen as in-progress so we will need to update the boolean variable we already created. This function originally does not have our variable in use, so we shall include it at the end of this function, like so:

// ACCEPT INCOMING CALL
function acceptIncomingCall(call) {
  call.accept();
  //update UI
  log("Accepted incoming call.");
  incomingCallAcceptButton.classList.add("hide");
  incomingCallRejectButton.classList.add("hide");
  incomingCallHangupButton.classList.remove("hide");
  callStarted=true;  //The new line we added
}

Once you have completed that, your JS file should now look just like the following found on my Github repository. Feel free to save your project and spin up ngrok and NPM to take a look at your new application.

Programmable Voice JavaScript Quickstart with a Dialpad in the UI

If you need instructions on how to deploy this application, please see the repo above or check out our other blog post.

Adding a UI to the Twilio JavaScript Quickstart

Congratulations! You now have a working Twilio Programmable Voice Quickstart app, with a convenient UI tacked on.

I hope you found this content useful. We cannot wait to see what you build next!

David Dooley is a Developer Support Engineer at Twilio based out of Dublin. He’s very passionate about helping our customers achieve success with Twilio and loves to work on complex problems. He also enjoys educating others, hence this blog post. In his spare time he likes to dabble in crypto and is a strong believer in blockchain technology. He can be reached at ddooley [at] twilio.com.