Friday, May 19, 2017

How to use Ajax and JSON for making dropdown menu?

Leave a Comment

Here is the code I used to show a category menu in OpenCart with different levels. It works, but after each click it produces more and more XHR finished loading: POST and XHR finished loading: GETs which stops the page by clicking sometimes:

<script type="text/javascript">  _url = '';   $(document).ready(function(){                     $('#mnav a').on('click', function() {         var cat = $(this).attr('id');         _url = '&category_id=' + cat;          $.post('index.php?route=test/category/child' + _url,             function(data) {                if(data.length>10){                     $('#mnav #sub').remove();                     $(data).insertAfter($('#mnav #' + cat));                }             });     });  });  $.ajaxPrefilter(function( options, original_Options, jqXHR ) {     options.async = true; }); </script> 

HTML Codes:

<div id="mnav" class="list-group">   <?php foreach ($categories as $category) { ?>   <a id="<?php echo $category['category_id']; ?>" class="list-group-item active"><?php echo $category['name']; ?></a>   <?php } ?> </div> 

Controller codes:

<?php class ControllerTestCategory extends Controller {     public function index() {         if (isset($this->request->get['path'])) {             $parts = explode('_', (string)$this->request->get['path']);         } else {             $parts = array();         }          $data['category_id'] = 0;         if (isset($parts[0])) {             $data['category_id'] = $parts[0];         } else {             $data['category_id'] = 0;         }          if (isset($parts[1])) {             $data['child_id'] = $parts[1];         } else {             $data['child_id'] = 0;         }          $this->load->model('catalog/cat');          $data['categories'] = array();          $categories = $this->model_catalog_cat->getCategories(0);          foreach ($categories as $category) {             $children_data = array();              $filter_data = array(                 'filter_category_id'  => $category['category_id'],                 'filter_sub_category' => true             );              $data['categories'][] = array(                 'category_id' => $category['category_id'],                 'name'        => $category['name'],                 'children'    => $category['children'],                 'products'    => $category['products'],                 'href'        => $this->url->link('product/category', 'path=' . $category['category_id'])             );         }          $this->response->setOutput($this->load->view('test/category', $data));     }     public function child() {         if (isset($this->request->get['category_id'])) {             $this->load->model('catalog/cat');              $data['categories'] = array();              $categories = $this->model_catalog_cat->getCategories($this->request->get['category_id']);              $data['x'] = '<div id="sub">';              foreach ($categories as $category) {                 $data['x'] .= '<li>' . $category['name'] . '</li>';             }             $data['x'] .= '</div>';         } else {             $data['x'] = 'NA';         }         $this->response->setOutput($this->load->view('test/category', $data));     } } 

SQL codes:

public function getCategories($parent_id = 0) {     $sql = "SELECT c.category_id, c.parent_id, cd.name,         (SELECT COUNT(DISTINCT ch.category_id) from category ch where ch.parent_id = c.category_id and cd.language_id = '" . (int)$this->config->get('config_language_id') . "') as children";      $sql .= " , (SELECT COUNT(DISTINCT p.product_id)  FROM product p       LEFT JOIN product_description pd ON (p.product_id = pd.product_id)      LEFT JOIN product_to_category p2c ON (p2c.product_id = p.product_id)      LEFT JOIN category_path cp ON (cp.category_id = p2c.category_id)  WHERE      pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND      p.status = '1' AND      p.date_available <= NOW()) AS items";      $sql .= " FROM category c LEFT JOIN category_description cd ON (c.category_id = cd.category_id) WHERE c.parent_id = '" . (int)$parent_id . "' AND cd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND c.status = '1' ORDER BY c.sort_order, LCASE(cd.name)";      $query = $this->db->query($sql);     return $query->rows; } 

I'd highly appreciate if you kindly help me by providing all necessary JavaScript, jQuery and JSON codes because I know these subjects with little :-(

6 Answers

Answers 1

The problem might be that you are not preventing the default action of the anchor tag. Try adding event.preventDefault. This way the browser will fire the POST request, not the GET one.

Besides it'd be best to bind the event to the document not to the elements if you are adding new #mnav a elements via ajax.

 $(document).ready( function() {                     $(document).on('click', '#mnav a', function( event ) {         event.preventDefault();         // some code     });  }); 

Answers 2

I have faced this problem in my ajax driven project, i hope my solution may help you.This may prevent from creating more calls.

$(document).ready( function() {      $(document).off('click','#mnav a').on('click','#mnav a',function(e){       e.preventDefault();       // your stuff here     }); }); 

Answers 3

I think there might be a problem in javascript code side. I am assuming that your PHP and DB related code is throwing result properly. Better solution would be download ajax menu loader plugin from here

You can set your PHP code for result throwing in file so front will not disturb at all. You might need to follow HTML structure and you can update your CSS with new class name to apply your style. I hope it will be quicker to solve your issue.

Answers 4

You could store the result of the post request in a javascript array, so you could reuse it, see following please:

var cachedObj = [];    $(document).ready(function(){                    $('#mnav a').on('click', function() {      var cat = $(this).attr('id');      _url = '&category_id=' + cat;      getData(cat, _url); //<-- Get data from ajax or cache    });  });    //This function replaces the $.post call (just for example)   function dummyPost(id, url){    //url should be used to make the post call    var data = "<span class='sub'>Test " + id + "</span>";    return data;  }    function getData(id, url){    //Try to get data from cache    var data;    if(cachedObj[url]) {      data = cachedObj[url];      console.log("Data retrived from cache");    }    else {      data = dummyPost(id, url);      cachedObj[url] = data;      console.log("Data retrived from post");    }        $('#mnav .sub').remove();    //$(data).insertAfter($('#mnav #' + id));    $('#mnav #' + id).append($(data));  }
.sub{    color: red;    font-weight: bold;  }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <div id="mnav" class="list-group">    <a id="1" class="list-group-item active">One</a>    <a id="2" class="list-group-item active">Two</a>    <a id="3" class="list-group-item active">Three</a>  </div>

I've maked the dummyPost function that should be modified in order to do the post request.

You could see in my example's log, that the first time you click on a link it retrieves his submenu with a "post", the next times instead, it get data from the cached array cachedObj.

I hope it helps you. Bye.

Answers 5

You can do this by following the steps.

create drop-down with javascript:

function myFunction(){  var port_button = document.getElementById("port").value;  if(port_button == 0){  var newhref;  var newhrefid;  var name = ["jquery ajax", "dropdown menu", "db"];  var links = ["api.jquery.com/jquery.ajax/", "www.w3schools.com/howto/howto_js_dropdown.asp", "www.w3schools.com/php/php_ajax_database.asp"];  var div=document.getElementById("myDropdown");   for(var i = 0; i<3; i++){    newhref= document.createElement("a");    newhref.href="http://"+links[i];    newhref.innerHTML= name[i];    newhrefid = "idhr_"+i;    newhref.setAttribute('id', newhrefid );    div.appendChild(newhref);  }  document.getElementById("myDropdown").classList.toggle("show");  document.getElementById("port").value = "2";  }  else if(port_button == 1){  document.getElementById("myDropdown").classList.toggle("show");  document.getElementById("port").value = "2";  }  else{  document.getElementById("myDropdown").classList.toggle("hide");  document.getElementById("port").value = "1";  }      }      function filterFunction() {      var input, filter, ul, li, a, i;      input = document.getElementById("myInput");      filter = input.value.toUpperCase();      div = document.getElementById("myDropdown");      a = div.getElementsByTagName("a");      for (i = 0; i < a.length; i++) {          if (a[i].innerHTML.toUpperCase().indexOf(filter) > -1) {              a[i].style.display = "";          } else {              a[i].style.display = "none";          }      }  }
.dropbtn {      background-color: #4CAF50;      color: white;      padding: 16px;      font-size: 16px;      border: none;      cursor: pointer;  }    .dropbtn:hover, .dropbtn:focus {      background-color: #3e8e41;  }  #myInput {      border-box: box-sizing;      background-image: url('searchicon.png');      background-position: 14px 12px;      background-repeat: no-repeat;      font-size: 16px;      padding: 14px 20px 12px 45px;      border: none;  }    .dropdown {      position: relative;      display: inline-block;  }    .dropdown-content {      display: none;      position: absolute;      background-color: #f6f6f6;      min-width: 230px;      overflow: auto;      box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);      z-index: 1;  }    .dropdown-content a {      color: black;      padding: 12px 16px;      text-decoration: none;      display: block;  }    .dropdown a:hover {background-color: #ddd}    .show {display:block;}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>  <div class="dropdown">  <button onclick="myFunction()" class="dropbtn">Dropdown</button>    <div id="myDropdown" class="dropdown-content">       <input type="text" placeholder="Search.." id="myInput" onkeyup="filterFunction()">    </div>  </div>  <input type="hidden" id = "port" value = "0">

You can create a dropdown of elements like 'input', 'p', 'a', and others.

Add this html to your code if you want to select a specific ID:

<form> <select id = "name_of_user" name="users" onchange="showUser(this.value)">   <option value="">Select a person:</option>   <option value="1">Peter Griffin</option>   <option value="2">Lois Griffin</option>   <option value="3">Joseph Swanson</option>   <option value="4">Glenn Quagmire</option>   </select> </form> 

To put database data in select join this code:

<?php $servername = "localhost"; $username = "username"; $password = "password"; $dbname = "myDB";  // Create connection $conn = new mysqli($servername, $username, $password, $dbname); // Check connection if ($conn->connect_error) {     die("Connection failed: " . $conn->connect_error); }  $sql = "SELECT id, name FROM user"; $result = $conn->query($sql); if ($result->num_rows > 0) {     // output data of each row     $option = "";     while($row = $result->fetch_assoc()) {        $option .= '<option value = "'.$row["id"].'">'.ech$row["name"].'</option>'; }     } $conn->close(); ?> <form> <select id = "name_of_user" name="users" onchange="showUser(this.value)"> <?php echo $option; ?>      </select> </form> 

Now you need to create and join AJAX to the drop menu code:

function myFunction(){     var port_button = document.getElementById("port").value;     if(port_button == 0){     var newhref;     var newhrefid;     var div=document.getElementById("myDropdown");      var val_1 = document.getElementById("name_of_user");//If you want to do a where select.     $.ajax({         url: 'test.php',         type: 'POST',         datatype: 'Json',         data: {'q': val_1},         success: function (response) {            var newhref;            var newhrefid;            var div=document.getElementById("myDropdown");             for(var i = 0; i<response.nunber_of_rows; i++){              newhref= document.createElement("a");              newhref.href= response.tabel[i];              newhref.innerHTML= response.tabel[i];              newhrefid = "idhr_"+i;              newhref.setAttribute('id', newhrefid );              div.appendChild(newhref);            }          }     });      document.getElementById("myDropdown").classList.toggle("show");  else if(port_button == 1){     document.getElementById("myDropdown").classList.toggle("show");     document.getElementById("port").value = "2";  }  else{     document.getElementById("myDropdown").classList.toggle("hide");     document.getElementById("port").value = "1";      }   } 

You need your php to connect to the database.

<?php $q = intval($_POST['q']); $error_state = ""; $con = mysqli_connect('localhost','peter','abc123','my_db'); if (!$con) {     die('Could not connect: ' . mysqli_error($con)); }  mysqli_select_db($con,"ajax_demo"); $sql="SELECT links FROM user WHERE id = '".$q."'"; $result = mysqli_query($con,$sql); $i = 0; while($row = mysqli_fetch_array($result)) {  $tabel[$i] = array($row['link']);  $i++; } $nunber_of_rows=$i; mysqli_close($con); echo json_encode (array(                     'tabel'=>$tabel,                     'nunber_of_rows'=>$nunber_of_rows )); ?> 

The sources for my example are in the links in the snippet example.

Any doubt, just ask for my help.

Answers 6

Before writing any code, it looks like you need to strategize about how you want the client and server to communicate.

My understanding of your problem is "the menu generates ajax requests with every click, and I don't want it to."

But this is how you've built the code: the menu's onclick handler set up by jQuery has a $.post() call in it.

The other way to do it is to send all the data necessary to populate the menu to the client up front. Then with every menu click, draw from data that's already in memory rather than sending an ajax request.

There are a few ways to do this I can think of. Which strategy you choose depends on your comfort and/or control over the system, and your priorities for page speed.

  1. Have the server prepare this data, and write it into the server-side template so the data is immediately available, something like <script><?php echo 'var menuData = '.json_encode($my_data).'; ?></script>. This is the fastest option.
  2. Use your existing ajax URLs, but fire off a series of ajax requests when the page loads. Send all the results to some central data object, like in option 1. Once all the data returns, your menu will work. The advantage here is you don't have to change any server side code, but lots of calls will take time.
  3. Create a new ajax URL that is able to return all the menu data in one batch. This would be faster than option 2, but you'd have to work on your server api.
If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment