Подтвердить что ты не робот

Как использовать Ajax и JSON для создания выпадающего меню?

Вот код, который я использовал для отображения меню категорий в OpenCart с разными уровнями. Он работает, но после каждого щелчка он производит все больше XHR finished loading: POST и XHR finished loading: GET, который останавливает страницу, нажимая иногда:

<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:

<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>

Коды контроллера:

<?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-коды:

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;
}

Я был бы очень признателен, если вы любезно поможете мне, предоставив все необходимые коды JavaScript, jQuery и JSON, потому что я знаю эти предметы с небольшим: - (

4b9b3361

Ответ 1

Вы можете сохранить результат запроса на отправку в массиве javascript, чтобы его можно было повторно использовать, см. ниже:

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>

Ответ 2

Проблема может заключаться в том, что вы не предотвращаете действие по умолчанию для тега привязки. Попробуйте добавить event.preventDefault. Таким образом браузер будет запускать запрос POST, а не GET.

Кроме того, было бы лучше привязать событие к документу не к элементам, если вы добавляете новые элементы #mnav a через ajax.

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

Ответ 3

Я столкнулся с этой проблемой в моем проекте, управляемом ajax. Надеюсь, мое решение может вам помочь. Это может помешать создавать больше вызовов.

// DOM ready
$(function() {

    $(document).off('click', '#mnav a').on('click', '#mnav a', function(e){

      e.preventDefault();

      // your stuff here

    });
});

Ответ 4

Прежде чем писать какой-либо код, похоже, вам нужно разработать стратегию о том, как вы хотите, чтобы клиент и сервер обменивались данными.

Мое понимание вашей проблемы заключается в том, что "меню генерирует аякс-запросы с каждым щелчком, и я не хочу этого".

Но так вы создали код: в меню onclick handler, настроенном jQuery, есть вызов $.post().

Другой способ сделать это - отправить все данные, необходимые для заполнения меню клиенту спереди. Затем с каждым щелчком меню нарисуйте данные, которые уже находятся в памяти, вместо отправки запроса ajax.

Есть несколько способов сделать это, о чем я могу думать. Какую стратегию вы выбираете, зависит от вашего комфорта и/или контроля над системой, а также ваших приоритетов для скорости страницы.

  • Попросите сервер подготовить эти данные и записать их в шаблон на стороне сервера, чтобы данные были сразу доступны, например <script><?php echo 'var menuData = '.json_encode($my_data).'; ?></script>. Это самый быстрый вариант.
  • Используйте существующие URL-адреса ajax, но при загрузке страницы вы запускаете ряд запросов ajax. Отправьте все результаты на некоторый центральный объект данных, как в варианте 1. После того как все данные вернутся, ваше меню будет работать. Преимущество здесь заключается в том, что вам не нужно менять какой-либо код на стороне сервера, но для многих вызовов потребуется время.
  • Создайте новый URL-адрес ajax, который сможет возвращать все данные меню в одной партии. Это будет быстрее, чем вариант 2, но вам придется работать на сервере api.

Ответ 5

Вы можете сделать это, выполнив следующие шаги.

создать раскрывающийся список с помощью 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">

Ответ 6

Я думаю, что может возникнуть проблема со стороны JavaScript. Я предполагаю, что ваш код, связанный с PHP и DB, корректно отображает результат. Лучшим решением будет загрузка плагина загрузчика ajax из здесь

Вы можете установить свой PHP-код для выдачи результата в файле, поэтому фронт не будет мешать вообще. Вам может потребоваться следовать структуре HTML, и вы можете обновить свой CSS с новым именем класса, чтобы применить свой стиль. Я надеюсь, что будет быстрее решить вашу проблему.