Click event on button doesn't seem to fire

I'm not new to HTML or JavaScript, though I don't use them often. I am, however, very new to PHP, and am currently facing a weird issue when trying to get a click event on a button in my HTML which is imbedded in PHP:

I'm not new to HTML or JavaScript, though I don't use them often. I am, however, very new to PHP, and am currently facing a weird issue when trying to get a click event on a button in my HTML which is imbedded in PHP:

<?php
session_start();
if(!(IsSet($_SESSION["username"])) && $_SESSION["username"] == "") {
?>
<!DOCTYPE html>
<html lang="de">
<head>
<title>Blah</title>
<meta charset="utf-8">
</head>
<body>
<button type="button" onclick="function() { alert('Moo!'); }">Some button</button>
</body>
</html>
<?php
} else {
}
?>

Nothing happens when I click on the button. Yes, there's of course more markup in the original, but it's not relevant to my question. The W3C markup validation service says there are no errors, and when I alert moo from a script at the bottom of the body tag it works fine as well. It looks like the click event doesn't fire... Any idea why?

Autocomplete Textbox with Javascript, PHP and MySQL

Autocomplete Textbox with Javascript, PHP and MySQL

In this guide, we will walk through how to add autocomplete to a textbox, with the help of jQuery and also an alternative that works with just vanilla Javascript.

Welcome to a step-by-step tutorial on how to create an autocomplete textbox with PHP, Javascript, and MySQL. In this guide, we will walk through how to add autocomplete to a textbox, with the help of jQuery and also an alternative that works with just vanilla Javascript. Read on to find out!

Quick notes
  • Download and unzip the file into a folder.
  • Create a dummy database and import 1-users.sql.
  • Change the database settings in 2-search.php to your own.
  • Access 3a-jquery.html in your browser for the jQuery version.
  • Access 3b-vanilla.html in your browser for the vanilla Javascript version.
1. Step 1: The Database

For the purpose of demonstration in this tutorial, I have included a simple user table and some sample data. You can skip this part if you already have an established database and want to work on that instead.

The User table and Sample data

CREATE TABLE `users` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `email` varchar(255) NOT NULL,
  `phone` varchar(24) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `users` (`id`, `name`, `email`, `phone`) VALUES
(1, 'Jazz Doe', '[email protected]', '56973317'),
(2, 'Jane Doe', '[email protected]', '40378541'),
(3, 'Rusty Terry', '[email protected]', '34614899'),
(4, 'Peers Sera', '[email protected]', '13014383'),
(5, 'Jaslyn Keely', '[email protected]', '52154191'),
(6, 'Richard Breann', '[email protected]', '58765281'),
(7, 'Imogene Thad', '[email protected]', '11753471'),
(8, 'Tillie Sharalyn', '[email protected]', '33989432'),
(9, 'Drake Adelaide', '[email protected]', '56539890'),
(10, 'Coby Kelleigh', '[email protected]', '83788228'),
(11, 'Frank Doe', '[email protected]', '87074336'),
(12, 'John Doe', '[email protected]', '55163490'),
(13, 'Jan Doe', '[email protected]', '45310572'),
(14, 'Joe Doe', '[email protected]', '48938630'),
(15, 'Joanne Doe', '[email protected]', '44101677'),
(16, 'Johan Doe', '[email protected]', '28278966'),
(17, 'Charles Doe', '[email protected]', '45800899'),
(18, 'Charlie Doe', '[email protected]', '32644564'),
(19, 'Apple Doe', '[email protected]', '76438908'),
(20, 'Orange Doe', '[email protected]', '47648224');

This is nothing but a very simple users table with 4 fields – id is the primary key, name, email, and phone.

Index your Searchable Fields

ALTER TABLE `users`
  ADD PRIMARY KEY (`id`),
  ADD UNIQUE KEY `email` (`email`),
  ADD KEY `name` (`name`);

ALTER TABLE `users`
MODIFY `id` int(21) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=21;

One small suggestion for you guys who already have an existing project – Index the fields that you want to search. Just why do we need to index the fields? Imagine a library that does not have a catalog system… Searching for a book that you want will mean having to look through each and every book in the library until you find it.

It is the same here in a database. If you don’t index the field, the database will have to look through each and every entry to find the ones that you want. Yes, while indexing does take up a little more disk space, but it will also speed up your searches and consume fewer system resources in the long run. Especially if you have a massive database with thousands of entries.

2. Step 2: Server - side script

Now that we have established the dummy database, it is time to work on a server-side script that will deal with searching the database and output the results.

Server side search

<?php
// YOU MIGHT WANT TO ADD SOME SECURITY ON THIS PAGE
// E.G. CHECK IF VALID USER LOGIN
// if (isset($_SESSION['admin'])) { ... DO SEARCH AS BELOW ... }
 
// (1) CONNECT TO DATABASE
$host = '127.0.0.1';
$dbname = 'test';
$user = 'root';
$password = '';
$charset = 'utf8';
$pdo = new PDO(
	"mysql:host=$host;dbname=$dbname;charset=$charset", $user, $password, [
		PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
		PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
		PDO::ATTR_EMULATE_PREPARES => false,
	]
);

// (2) SEARCHING FOR?
$data = [];
switch ($_POST['type']) {
  // (2A) INVALID SEARCH TYPE
  default :
    break;
 
  // (2B) SEARCH FOR USER
  case "user":
    // You might want to limit number of results on massive databases
    // SELECT * FROM XYZ WHERE `FIELD` LIKE ? LIMIT 20
    $stmt = $pdo->prepare("SELECT * FROM `users` WHERE `name` LIKE ?");
    $stmt->execute(["%" . $_POST['term'] . "%"]);
    while ($row = $stmt->fetch(PDO::FETCH_NAMED)) {
      $data[] = $row['name'];
    }
    break;
 
  // (2C) SEARCH FOR EMAIL
  case "email":
    $stmt = $pdo->prepare("SELECT * FROM `users` WHERE `email` LIKE ?");
    $stmt->execute(["%" . $_POST['term'] . "%"]);
    while ($row = $stmt->fetch(PDO::FETCH_NAMED)) {
      $data[] = $row['email'];
    }
    break;
}

// (3) RETURN RESULT
$pdo = null;
echo json_encode($data);
?>
  • There are 2 things to POST to this script.
    • First, which type of data you want – user or email?
    • A search term should also be sent to this script.
  • Very straightforward. The script first opens up a database connection – Remember to change the database settings to your own.
  • Then runs a search query on the user table, depending on what you want to look for.
  • Finally, outputs the search results (if any) using JSON encoding.

The jQuery autocomplete plugin uses JSON, but if you want to work with XML (or some other format) in your own project, please feel free to modify the code to fit your own needs.

3. Step 3: Jquery Autocomplete

The final piece of the puzzle is the client-side HTML and Javascript, which I will give 2 alternatives for. This first alternative includes the use of jQuery UI, and it is as simple as attaching the autocomplete to an element.

The Script:

<!DOCTYPE html>
<html>
  <head>
    <title>
      jQuery Autocomplete Demo
    </title>
    <!-- (1) YOU NEED TO LOAD BOTH JQUERY AND JQUERY UI -->
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>ry-ui.min.js"></script>

    <!-- (2) ATTACH AUTOCOMPLETE ON PAGE LOAD -->
    <script>
      $(function () {
        // NAME AUTO-COMPLETE
        $('#user-name').autocomplete({
          source: function (request, response) {
            $.ajax({
              type: "POST",
              url: "2-search.php",
              data: {
                term: request.term,
                type: "user"
              },
              success: response,
              dataType: 'json',
              minLength: 2,
              delay: 100
            });
          }
        });

        // EMAIL AUTO-COMPLETE
        $('#user-email').autocomplete({
          source: function (request, response) {
            $.ajax({
              type: "POST",
              url: "2-search.php",
              data: {
                term: request.term,
                type: "email"
              },
              success: response,
              dataType: 'json',
              minLength: 2,
              delay: 100
            });
          }
        });
      });
    </script>
  </head>
  <body>
    <!-- (3) DUMMY FORM -->
    Name: <input type="text" id="user-name"/>
    <br/><br/>
    Email: <input type="email" id="user-email"/>
  </body>
</html>

The Explannation

Not much rocket science is required when it comes to jQuery:

  • You need to load both the main jQuery library and jQuery UI.
  • Just attach autocomplete to the input fields.

Please do also check out the jQuery autocomplete documentation on the settings that you can use.

4. Step 4: Vanila JavaScript

For you guys who do not wish to use jQuery, or think that it adds too much to the loading bloat, here is the other alternative written in pure Javascript.

The HTML:

<!DOCTYPE html>
<html>
  <head>
    <title>
      Vanilla Javascript Autocomplete Demo
    </title>
    <!-- (1) AUTOCOMPLETE SCRIPTS -->
    <link href="3b-autocomplete.css" rel="stylesheet">
    <script src="3b-autocomplete.js"></script>
 
    <!-- (2) ATTACH AUTOCOMPLETE ON PAGE LOAD -->
    <script>
    window.addEventListener("load", function(){
      // NAME AUTO-COMPLETE
      suggest.attach({
        // Minimum required
        target : "user-name",
        url : "2-search.php",
        data : { type: "user" }
      });
 
      // EMAIL AUTO-COMPLETE
      suggest.attach({
        // Minimum required
        target : "user-email",
        url : "2-search.php",
        data : { type: "email" },
        // Optional
        delay : 200,
        min : 3
      });
    });
    </script>
  </head>
  <body>
    <!-- (3) DUMMY FORM -->
    Name: <input type="text" id="user-name"/>
    <br/><br/>
    Email: <input type="email" id="user-email"/>
  </body>
</html>
  • Text fields in HTML as usual.
  • We wait for the page to be fully loaded window.addEventListener("load") before attaching the autocomplete to the fields.
  • Use suggest.attach(OPTIONS) to attach the auto-suggest to the required fields.

The CSS

div.acWrap {
  position: relative;
}
div.acBox {
  position: absolute;
  top: 25px;
  left: 0;
  padding: 5px;
  z-index: 999;
  background: #fff;
  border: 1px solid #000;
  display: none;
}
div.acBox div:hover {
  cursor: pointer;
  background: #fea;
}

Now, the autocomplete script will “pad” the input field with some HTML `` wrappers, and it will look like this when fully initiated:

<div id="acWrapXXX" class="acWrap">
  <input type="text" id="FOO-BAR"/>
  <div id="acBoxXXX" class="acBox">
    <!-- SUGGESTIONS ARE PUT HERE DYNAMICALLY VIA AJAX -->
    <div>Option A</div>
    <div>Option B</div>
    <div>Option C</div>
  </div>
</div>

So feel free to change the CSS styles to suit your own project.

The JavaScript:

var suggest = {
  instance : {}, // attached instances
  focus : null, // current text field in focus

  attach : function (opt) {
  // suggest.attach () : attach autosuggest to input field
  // opt : options
  //  - target : ID of target input field, required
  //  - url : Target URL to fetch suggestions, required
  //  - delay : Delay before autocomplete fires up, optional, default 500ms
  //  - min : Minimum characters to fire up autocomplete, default 2
  //  - data : Additional data to post to the server
  //  - fetch : Manual fetch draw override
  //  - select : Manual select draw override

    // Create autocomplete wrapper and box
    var id = Object.keys(suggest.instance).length,
        input = document.getElementById(opt.target);
    input.outerHTML = "<div id='acWrap" + id + "' class='acWrap'>" + input.outerHTML + "<div id='acBox" + id + "' class='acBox'></div></div>";

    // Set the HTML references and options
    suggest.instance[opt.target] = {
      input : document.getElementById(opt.target),
      wrap : document.getElementById("acWrap" + id),
      box : document.getElementById("acBox" + id),
      delay : opt.delay ? opt.delay : 500,
      url : opt.url,
      min : opt.min ? opt.min : 2,
      data : opt.data ? opt.data : null,
      fetch : opt.fetch ? opt.fetch : null,
      select : opt.select ? opt.select : null,
      timer : null
    };
 
    // Attach key listener
    suggest.instance[opt.target].input.addEventListener("keyup", function (evt) {
      // Clear old timer
      if (suggest.instance[opt.target].timer != null) {
        window.clearTimeout(suggest.instance[opt.target].timer);
      }

      // Hide and clear old suggestion box
      suggest.instance[opt.target].box.innerHTML = "";
      suggest.instance[opt.target].box.style.display = "none";

      // Create new timer, only if minimum characters
      if (evt.target.value.length >= suggest.instance[opt.target].min) {
        suggest.instance[opt.target].timer = setTimeout(
          function () { suggest.fetch(evt.target.id); },
          suggest.instance[opt.target].delay
        );
      }
    });

    // This is used to hide the suggestion box if the user navigates away
    suggest.instance[opt.target].input.addEventListener("focus", function (evt) {
      if (suggest.focus != null) { suggest.close(null, true); }
      suggest.focus = opt.target;
    });
  },

  fetch : function (id) {
  // suggest.fetch() : AJAX get suggestions and draw
  // id : ID of target input field, automatically passed in by keyup event

    // Init AJAX
    var req = new XMLHttpRequest();
    req.addEventListener("load", function () {
      var data = JSON.parse(this.response);
      if (Object.keys(data).length > 0) {
        if (suggest.instance[id]['fetch']) {
          suggest.instance[id]['fetch'](id, data);
        } else {
          data.forEach(function (el) {
            suggest.instance[id].box.insertAdjacentHTML("beforeend", "<div onclick=\"suggest.select('" + id + "', this);\">" + el + "</div>");
          });
        }
        suggest.instance[id].box.style.display = "block";
        document.addEventListener("click", suggest.close);
      }
    });

    // Data
    var data = new FormData();
    data.append('term', suggest.instance[id].input.value);
    if (suggest.instance[id].data) {
      for (let key in suggest.instance[id].data) {
        data.append(key, suggest.instance[id].data[key]);
      }
    }

    // Send
    req.open("POST", suggest.instance[id].url);
    req.send(data);
  },

  select : function (id, el, more) {
  // suggest.select() : user selects a value from autocomplete
  // id : ID of text box
  // el : Selected element
  // more : Optional, more parameters
 
    if (suggest.instance[id]['select']) {
      suggest.instance[id]['select'](id, el, more);
    } else {
      suggest.instance[id].input.value = el.innerHTML;
      suggest.instance[id].box.innerHTML = "";
      suggest.instance[id].box.style.display = "none";
      document.removeEventListener("click", suggest.close);
    }
  },

  close : function (evt, force) {
  // suggest.close() : close the autocomplete box if the user clicks away from the input field
  // evt : click event
  // force : force close
 
    if (force || event.target.closest(".acWrap") == null) {
      suggest.instance[suggest.focus].box.innerHTML = "";
      suggest.instance[suggest.focus].box.style.display = "none";
      document.removeEventListener("click", suggest.close);
    }
  }
};

The Explannation:

This may look like a handful at first, but it is a stepped-down version of what jQuery has. Rather than repeating line-by-line (trace through the code on your own if you want)

5. Step 5: Jquery Complete Multiple

The autocomplete is done, and here is an extra section for you guys who want to populate many fields with one selection. For example, select the user name, and automatically fill up the email plus phone number.

Sever-Side Addition

Firstly, we are going to add an extra handler in the server-side search script:

<?php
switch ($_POST['type']) {
  // (X) SEARCH FOR USER WITH ALL INFORMATION
  // JQUERY VERSION
  case "user-all":
    // Data yoga
    $data = [
      "display" => [],
      "details" => []
    ];

    $stmt = $pdo->prepare("SELECT * FROM `users` WHERE `name` LIKE ?");
    $stmt->execute(["%" . $_POST['term'] . "%"]);
    while ($row = $stmt->fetch(PDO::FETCH_NAMED)) {
      $data["display"][] = [
        "label" => $row['name'],
        "value" => $row['id']
      ];
      $data["details"][$row['id']] = [
        "email" => $row['email'],
        "phone" => $row['phone']
      ];
    }
    break;
}

Now, this is pretty much the same “search the database by user” as above, but we are returning a lot more data this time around. Take note of how the $data response is formed:

  • $data["display"] will hold a list of the user id and name.
  • $data["details"] will hold a list of the user id, email and phone.
  • Yep, I would have just packed everything flat inside $data, but jQuery works otherwise.
  • In jQuery autocomplete, it takes an array of label and value to generate the autocomplete list – This is why we have $data["display"], with the user name as the label, and id as the value.
  • Upon selection, as you can guess, we will then use the selected value (id) to tie to $data["details"] – Use it to populate the rest of the fields.

The HTML

<!DOCTYPE html>
<html>
  <head>
    <title>
      jQuery Autocomplete Demo
    </title>

    <!-- (1) YOU NEED TO LOAD BOTH JQUERY AND JQUERY UI -->
    <link rel="stylesheet" href="https://code.jquery.com/ui/1.12.1/themes/base/jquery-ui.css">
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>

    <!-- (2) ATTACH AUTOCOMPLETE ON PAGE LOAD -->
    <script>
      // GLOBAL TEMP VAR TO HOLD AUTOCOMPLETE DATA
      var actemp = null;

      // AUTO-COMPLETE SETUP
      $(function () {
        $('#user-name').autocomplete({
          source: function (request, response) {
            $.ajax({
              type: "POST",
              url: "2-search.php",
              data: {
                term: request.term,
                type: "user-all"
              },
              success: function(data) {
                // Details to global temp
                actemp = data.details;

                // Return display
                response(data.display);
              },
              dataType: 'json',
              minLength: 2,
              delay: 100
            });
          },
          select : function(event, ui){
            // Fill up other fields
            $('#user-name').val(ui.item.label);
            $('#user-email').val(actemp[ui.item.value]['email']);
            $('#user-tel').val(actemp[ui.item.value]['phone']);
            return false;
          }
        });
      });
    </script>
  </head>
  <body>
    <!-- (3) DUMMY FORM -->
    Name: <input type="text" id="user-name"/>
    <br/><br/>
    Email: <input type="email" id="user-email"/>
    <br/><br/>
    Tel: <input type="email" id="user-tel"/>
  </body>
</html>

A little bit confusing in this one, but it is as described above. We use data["display"] to generate the selection list, use the selected value to tie back to data["details"], then it is possible to auto-complete the rest of the other fields.

6. Step 6: Vanilla Complete Multiple

Finally, the “auto-complete many” vanilla Javascript version.

Sever-Side Addition

Just like the jQuery version, we are going to add another request to the server-side script. But this time, it will be a lot more straightforward for the vanilla version.

<?php
switch ($_POST['type']) {
  // (X) SEARCH FOR USER WITH ALL INFORMATION
  // VANILLA VERSION
  case "user-a":
    $stmt = $pdo->prepare("SELECT * FROM `users` WHERE `name` LIKE ?");
    $stmt->execute(["%" . $_POST['term'] . "%"]);
    while ($row = $stmt->fetch(PDO::FETCH_NAMED)) {
      $data[$row["id"]] = $row;
    }
    break;
}

Override Draw

Finally, all we have to do is to override the draw and select to pick up the additional data.

<!DOCTYPE html>
<html>
  <head>
    <title>
      Vanilla Javascript Autocomplete Demo
    </title>

    <!-- (1) AUTOCOMPLETE SCRIPTS -->
    <link href="3b-autocomplete.css" rel="stylesheet">
    <script src="3b-autocomplete.js"></script>

    <!-- (2) ATTACH AUTOCOMPLETE ON PAGE LOAD -->
    <script>
    // GLOBAL TEMP VAR TO HOLD AUTOCOMPLETE DATA
    var actemp = null;
    window.addEventListener("load", function(){

      // NAME AUTO-COMPLETE
      suggest.attach({
        target : "user-name",
        url : "2-search.php",
        data : { type: "user-a" },

        // MANUAL OVERRIDE FETCH
        fetch : function (id, data) {
          // User data to global temp
          actemp = data;

          // Suggestion draw override
          for (let uid in data) {
            suggest.instance[id].box.insertAdjacentHTML("beforeend", "<div onclick=\"suggest.select('" + id + "', this, " + uid + " );\">" + data[uid]['name'] + "</div>");
          }
        },

        // Selection draw override
        select : function (id, el, more) {
          // Name
          suggest.instance[id].input.value = el.innerHTML;
          // Email
          document.getElementById("user-email").value = actemp[more]['email'];
          // Phone
          document.getElementById("user-tel").value = actemp[more]['phone'];
          // Close suggestions
          suggest.instance[id].box.innerHTML = "";
          suggest.instance[id].box.style.display = "none";
          document.removeEventListener("click", suggest.close);
        }
      });
    });
    </script>
  </head>
  <body>
    <!-- (3) DUMMY FORM -->
    Name: <input type="text" id="user-name"/>
    <br/><br/>
    Email: <input type="email" id="user-email"/>
    <br/><br/>
    Tel: <input type="email" id="user-tel"/>
  </body>
</html>

That’s all folks. The vanilla version is simpler than jQuery for once.

What next?

Thank you for reading, and we have come to the end of this short tutorial. I hope it has given you a kick start to your own project, and if you decide to use my vanilla auto-complete, please do remember to do your own CSS styles with it… It is really nothing but a skeleton.

If there is anything that you like to add to this guide, please feel free to comment below. Good luck and happy coding!

Render HTML with Vanilla JavaScript and lit-html

Render HTML with Vanilla JavaScript and lit-html

Sometimes you need to render HTML elements on a web page. And like Goldilocks' search for "just right", you have to try a few techniques before you find the right one. Using a framework may be too hard. Using pure HTML and the DOM API may be too soft. What you need is something in the middle that is just right. Is lit-html "just right"? Let's find out.

Sometimes you need to render HTML elements on a web page. And like Goldilocks' search for "just right", you have to try a few techniques before you find the right one. Using a framework may be too hard. Using pure HTML and the DOM API may be too soft. What you need is something in the middle that is just right. Is lit-html "just right"? Let's find out.

First, I'll show how this all works. Then at the end of this article, I'll explain everything you need to get started with lit-html to try this for yourself.

When you're done, you can push your HTML app with lit-html to the cloud to see it in all of its glory! I included a link to a free Azure trial, so you can try it yourself.

Resources:

The Sample App

Here is the app I'll demonstrate in this article. It fetches a list of heroes and renders them when you click the button. It also renders a progress indicator while it is fetching.

What's the Value of lit-html

When you focus on rendering content, and nothing else, lit-html is a good fit. It works closely with the DOM to render content, and refresh it in an optimal manner. The docs can provide you with more details, but the basic code for lit-html looks like this.

// Credit: As seen in official docs https://lit-html.polymer-project.org/guide/getting-started

// Import lit-html
import { html, render } from 'lit-html';

// Define a template
const myTemplate = name =>
  html`
    <p>Hello ${name}</p>
  `;

// Render the template to the document
render(myTemplate('World'), document.body);

You import lit-html, define a template, then render it to the DOM. That's it!

Rendering HTML

A progress bar is fairly basic. There is some HTML, and we show it when needed and hide it when it is not required. While we could use a template, or innerHTML, or the DOM API for this, let's see what this would look like with lit-html.

First, we get a reference to the element in the DOM where the progress bar will appear.

Then we define the template. This code looks and feels like JSX (or TSX). The advantage here is that you can write the HTML. You wrap the HTML in a template string (notice the back-tick character is used and not a single quote). Template strings allow you to span lines and insert variables where needed (we'll see this soon). The magic that makes this work is the html tag that precedes the template string. The html tag is what tells lit-html that you are about to define a template.

Next, we compile the template and pass those results to lit-html's render function, which places the results in the DOM. Finally, we hide or show the progress bar as needed.

function showProgress(show = true) {
  const container = document.getElementById('progress-placeholder');

  const template: () => TemplateResult = () => html`
    <progress class="progress is-medium is-info" max="100"></progress>
  `;
  const result = template();
  render(result, container);

  container.style.display = show ? 'block' : 'none';
}

Now you can run this showProgress function any time you want to show the progress bar.

Note that when a template is re-rendered, the only part that is updated is the data that changed. If no data changed, nothing is updated.

Rendering HTML with Dynamic Values

The progress bar does not change each time it is rendered. You will have situations where you want your HTML to change. For example, you may have a message area on your web app that shows a styled message box with a title and a message. The title and message will change every time you show the message area. Now you have dynamic values.

The HTML is defined with a template string, so it is trivial to add a variable into it. Notice the code below adds a title and text into the template, using the ${data.title} and ${data.text} syntax, respectively.

Then the template is compiled and rendered were needed.

When this template is re-rendered, the only part that is updated is the data that changed. In this case, that's the title and text.

function showMessage(text: string, title = 'Info') {
  const template: (data: any) => TemplateResult = (data: Message) => html`
    <div id="message-box" class="message is-info">
      <h3 class="message-header">${data.title}</h3>
      <p class="message-body">${data.text}</p>
    </div>
  `;

  const el = document.getElementById('message-placeholder');
  const result = template({ title, text });
  render(result, el);

  el.style.visibility = !!text ? 'visible' : 'hidden';
}

Rendering a List

Things get a little more real when we render a list. Let's think about that for a moment. A list requires that we have a plan if there is data and a backup plan if there is no data. A list requires that we render the same thing for each row, and we don't know how many rows we have. A list requires that we pass different values for each row, too. Then we have to take the rows and wrap them in a container such as a <ul> or a <table>.

So there is a little more logic here, regardless of whether we use lit-html or any other technique. Let's explore how the replaceHeroList function renders the rows using lit-html.

function replaceHeroList(heroes?: Hero[]) {
 const heroPlaceholder = document.querySelector('.hero-list');

 // Define the template
 let template: () => TemplateResult;

 if (heroes && heroes.length) {
   // Create the template for every hero row
   template = createList();
 } else {
   // Create the template with a simple "not found" message
   template = () =>
     html`
       <p>heroes not found</p>
     `;
 }

 // Compile the template
 const result = template();

 // Render the template
 render(result, heroPlaceholder);

Notice that when there are heroes, we call the createList function. This function begins by creating an array of TemplateResult. So for every hero in the heroes array, we define a template that represents the <li> containing the HTML that displays that respective hero.

Then we create another template that contains the <ul> and embeds the array of hero templates. It's pretty cool that we can embed templates like this! Finally, we return it all and let the logic compile the templates and render them.

function createList() {
  // Create an array of the templates for each hero
  const templates: TemplateResult[] = heroes.map(hero => {
    return html`
      <li>
        <div class="card">
          <div class="card-content">
            <div class="content">
              <div class="name">${hero.name}</div>
              <div class="description">${hero.description}</div>
            </div>
          </div>
        </div>
      </li>
    `;
  });

  // Create a template that includes the hero templates
  const ulTemplate: () => TemplateResult = () =>
    html`
      <ul>
        ${templates}
      </ul>
    `;
  return ulTemplate;
}

Summary

When you want to render HTML, lit-html is a fast and light-weight option. Is it better than using templates and the DOM API? You'll have to decide what is best for you. But the real story here is that you have another great option to consider when determining the right tool for your job.

Prologue

You can also get editor help with your lit-html templates. Notice the image below shows the syntax highlighting for the HTML template!

Setup

You can install the lit-html package with npm.

npm install lit-html

Alternately you can load it directly from the unpkg.com CDN

import { html, render } from 'https://unpkg.com/lit-html?module';

You have a choice here. npm is my preference, but feel 100% free to use the CDN if that suits you.

TypeScript and lit-html

You only need to include the library for lit-html and you're done. But I like to use TypeScript, and I absolutely recommend enabling your tooling to work great with typeScript and lit-html.

Let me be very clear here - you do not need TypeScript. I choose to use it because it helps identify mistakes while I write code. If you don't want TypeScript, you can opt to use plain JavaScript.

Here are the steps to make TypeScript and lit-html light up together:

  1. Install TypeScript support for lit-html
  2. Configure your tsconfig.json file
  3. Install the VS Code extension for lit-html

Run this command to install the plugin and typescript, as development dependencies to your project.

npm install --save-dev typescript-lit-html-plugin typescript

Edit your tsconfig.json by adding the following to your compilerOptions section.

"compilerOptions": {
  "plugins": [
    {
      "name": "typescript-lit-html-plugin"
    }
  ]
}

Finally, install the VS Code extension for lit-html.

Now you get syntax highlighting for all of your lit-html templates!

How to Retrieve full Profile of LinkedIn User using Javascript

How to Retrieve full Profile of LinkedIn User using Javascript

I am trying to retrieve the full profile (especially job history and educational qualifications) of a linkedin user via the Javascript (Fetch LinkedIn Data Using JavaScript)

Here we are fetching LinkedIn data like Username, Email and other fields using JavaScript SDK.

Here we have 2 workarounds.

  1. Configuration of linkedIn developer api
  2. Javascript Code to fetch records

Configuration of linkedIn developer api

In order to fetch records, first we need to create developer api in linkedin which will act as token/identity while fetching data from other linkedin accounts.

So to create api, navigate to https://linkedin.com/developer/apps and click on 'Create Application'.

After navigating, fill in details like name, description and other required fields and then submit.

As we submit, it will create Client ID and Client Secret shown below, which we will be using in our code while communicating to fetch records from other LinkedIn account.

Note: We need to provide localhost Url here under Oauth 2.0. I am using my localhost, but you can probably use other production URLs under Oauth 2.0 where your app is configured. It will make your api  consider the Url as trusted which fetching records.

Javascript Code to fetch records

For getting user details like first name, last name,User image can be written as,

<script type="text/javascript" src="https://platform.linkedin.com/in.js">  
    api_key: XXXXXXX //Client ID  
    onLoad: OnLinkedInFrameworkLoad //Method that will be called on page load  
    authorize: true  
</script>  
<script type="text/javascript">  
    function OnLinkedInFrameworkLoad() {  
        IN.Event.on(IN, "auth", OnLinkedInAuth);  
    }  
  
    function OnLinkedInAuth() {  
        IN.API.Profile("me").result(ShowProfileData);  
    }  
  
    function ShowProfileData(profiles) {  
        var member = profiles.values[0];  
        var id = member.id;  
        var firstName = member.firstName;  
        var lastName = member.lastName;  
        var photo = member.pictureUrl;  
        var headline = member.headline;  
        //use information captured above  
        var stringToBind = "<p>First Name: " + firstName + " <p/><p> Last Name: " + lastName + "<p/><p>User ID: " + id + " and Head Line Provided: " + headline + "<p/>"  
        document.getElementById('profiles').innerHTML = stringToBind;  
    }  
</script>    

Kindly note we need to include 'https://platform.linkedin.com/in.js' as src under script type as it will act on this Javascript SDK provided by Linkedin.

In the same way we can also fetch records of any organization with the companyid as keyword.

<head>  
    <script type="text/javascript" src="https://platform.linkedin.com/in.js">  
        api_key: XXXXXXX ////Client ID  
        onLoad: onLinkedInLoad  
        authorize: true  
    </script>  
</head>  
  
<body>  
    <div id="displayUpdates"></div>  
    <script type="text/javascript">  
        function onLinkedInLoad() {  
            IN.Event.on(IN, "auth", onLinkedInAuth);  
            console.log("On auth");  
        }  
  
        function onLinkedInAuth() {  
            var cpnyID = XXXXX; //the Company ID for which we want updates  
            IN.API.Raw("/companies/" + cpnyID + "/updates?event-type=status-update&start=0&count=10&format=json").result(displayCompanyUpdates);  
            console.log("After auth");  
        }  
  
        function displayCompanyUpdates(result) {  
            var div = document.getElementById("displayUpdates");  
            var el = "<ul>";  
            var resValues = result.values;  
            for (var i in resValues) {  
                var share = resValues[i].updateContent.companyStatusUpdate.share;  
                var isContent = share.content;  
                var isTitled = isContent,  
                    isLinked = isContent,  
                    isDescription = isContent,  
                    isThumbnail = isContent,  
                    isComment = isContent;  
                if (isTitled) {  
                    var title = isContent.title;  
                } else {  
                    var title = "News headline";  
                }  
                var comment = share.comment;  
                if (isLinked) {  
                    var link = isContent.shortenedUrl;  
                } else {  
                    var link = "#";  
                }  
                if (isDescription) {  
                    var description = isContent.description;  
                } else {  
                    var description = "No description";  
                }  
                /* 
                if (isThumbnailz) { 
                var thumbnailUrl = isContent.thumbnailUrl; 
                } else { 
                var thumbnailUrl = "http://placehold.it/60x60"; 
                } 
                */  
                if (share) {  
                    var content = "<a target='_blank' href=" + link + ">" + comment + "</a><br>";  
                    //el += "<li><img src='" + thumbnailUrl + "' alt=''>" + content + "</li>";  
                    el += "<li><div>" + content + "</div></li>";  
                }  
                console.log(share);  
            }  
            el += "</ul>";  
            document.getElementById("displayUpdates").innerHTML = el;  
        }  
    </script>  
</body>  

We can get multiple metadata while fetching records for any any organization. We can get company updates as shown below.

Conclusion

We can also fetch any company specific data like company job updates/post, total likes, comments, and number of views along with a lot of metadata we can fetch which I have shown below.

Thank you for reading !