1

I am using autocomplete to get suggestions from multiple sources and show them as one list in the UI. When I am typing, the suggestions show up, but when I hover over a suggestion I get an error "TypeError: n is undefined". When I click a suggestion I get an error "ui.item is undefined".

The HTML page:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>jQuery Autocomplete with Multiple Search Engines Suggestions</title>

<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
<script type="text/javascript">
$.widget( "custom.catcomplete", $.ui.autocomplete, {
    _renderMenu: function( ul, items ) {
        var self = this, currentCategory = "";
        $.each( items, function( index, item ) {
            if ( item.category != currentCategory ) {
                ul.append( "<li class='ui-autocomplete-category'>" + item.category + "</li>" );
                currentCategory = item.category;
            }
            self._renderItem( ul, item );
        });
    }
});

$(function(){ //page load
    $("#q").focus(); //set focus to search field
    $("#q").catcomplete({
        source:"suggest.php",
        minLength:2,
        delay:10,
        select: function(event, ui) { 
            window.location.assign(ui.item.searchUrl + ui.item.label);
            //document.getElementById("q").value = ui.item.label
            //$("#q").val(ui.item.value);
            //$("#searchform").submit();
        }
    });
});
</script>
<style type="text/css">
.ui-autocomplete-category {
    font-weight: bold;
    font-size: 1em;
    padding: .2em .2em;
    margin: .4em 0 .2em;
    line-height: 1.5;
    color: #069;
    border-bottom: 2px solid #069;
}
li.ui-autocomplete-category {
    list-style-type: none;
}
</style>
</head>
<body>
    <form id="searchform" name="form1" method="get" action="http://www.google.com/search">
        Search: 
        <input name="q" id="q" type="text" size="40" />
        <input name="submit" type="submit" value="Search" />
    </form>
</body>
</html>

The PHP-script:

<?php
//Search term
$term = $_REQUEST['term'];
//Search Engine array
$searchEngines = array(
    "Google" => array("http://suggestqueries.google.com/complete/search?output=firefox&client=firefox&q=", "http://www.google.com/search?q="),
    //"Bing" => array("http://api.bing.com/osjson.aspx?query=", "http://www.bing.com/search?q="),
    //"Yahoo" => array("http://ff.search.yahoo.com/gossip?output=fxjson&command=", "http://search.yahoo.com/search?p="),
    "Wikipedia" => array("http://en.wikipedia.org/w/api.php?action=opensearch&search=", "http://en.wikipedia.org/w/index.php?title=Special%3ASearch&search="),
    //"Ebay" => array("http://anywhere.ebay.com/services/suggest/?q=", "http://shop.ebay.com/i.html?_nkw="),
    "Amazon" => array("http://completion.amazon.com/search/complete?search-alias=aps&client=amazon-search-ui&mkt=1&q=", "http://www.amazon.com/s/field-keywords=")

);

//Combine Search Results
$searchArray = array();
foreach($searchEngines as $engine => $urls){
    $url = $urls[0] . rawurlencode($term);
    try{
        //$json = file_get_contents($url);
        $json = get_url_contents($url);
        $array = json_decode($json);
        $array = $array[1]; //$array[1] contains result list
        if(count($array) > 0){
            $array = getFormattedArray($array, $engine, $urls[1]);
            $searchArray = array_merge($searchArray, $array );
        }
    } catch (Exception $e){ /* Skip the exception */ }
}

//Output JSON
header('content-type: application/json; charset=utf-8');
echo json_encode($searchArray); //Convert array to JSON object

//Format array to add category (search engine name)
function getFormattedArray($array, $engine, $searchUrl){
    $newArray = array();
    foreach($array as $a){
        $newArray[] = array('label' => $a, 'searchUrl' => $searchUrl, 'category' => $engine);
    }
    return $newArray;
}

//Read URL contents
function get_url_contents($url)
{
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
    $ip=rand(0,255).'.'.rand(0,255).'.'.rand(0,255).'.'.rand(0,255);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array("REMOTE_ADDR: $ip", "HTTP_X_FORWARDED_FOR: $ip"));
    curl_setopt($ch, CURLOPT_USERAGENT, "Mozilla/".rand(3,5).".".rand(0,3)." (Windows NT ".rand(3,5).".".rand(0,2)."; rv:2.0.1) Gecko/20100101 Firefox/".rand(3,5).".0.1");
    $html = curl_exec($ch);
    curl_close($ch);
    return $html;
}
?>
1
  • Can you provide an example or sample of the output from PHP? Commented Jan 17, 2017 at 16:26

1 Answer 1

2

I am adding this as a comment, not an answer, for the moment and I plan to update it as we get through the issue.

First, I was able to replicate your code and the error here: https://jsfiddle.net/xatu48sc/2/

I am using the regular (non minified) version. I encountered the error on Line 5836:

5826    item = ui.item.data( "ui-autocomplete-item" );
5827    if ( false !== this._trigger( "focus", event, { item: item } ) ) {
5828
5829        // use value to match what will end up in the input, if it was a key event
5830        if ( event.originalEvent && /^key/.test( event.originalEvent.type ) ) {
5831            this._value( item.value );
5832        }
5833    }
5834
5835    // Announce the value in the liveRegion
5836    label = ui.item.attr( "aria-label" ) || item.value;
5837    if ( label && $.trim( label ).length ) {
5838        this.liveRegion.children().hide();
5839        $( "<div>" ).text( label ).appendTo( this.liveRegion);
5840    }

The error I see is:

TypeError: item is undefined jquery-ui.js (line 5836, col 13)

This tells us that item is not defined, thus item.value is undefined, and so I included the code before where it's initiated. This will point us back to your custom widget to render categories.

When I compare that to the example at https://jqueryui.com/autocomplete/#categories I can see a number of differences. There is no _create method and in the _renderMenu method, there is no li defined for items.

Somewhere in here is where the problem lies.

I have also found that this error is only thrown for items that have no category.

UPDATE

I found the issue in your code by using a Text Compare site. Here is the issue:

self._renderItem( ul, item );

The command in the example page is:

li = that._renderItemData( ul, item );

It's not using _renderItem(), but what appears to be an undocumented extension point: _renderItemData()

Base on this: Difference between jQuery autocomplete renderItem and renderItemData you are using the extension point correctly.

When I make this minor change to your code, it works without error:

$.widget("custom.catcomplete", $.ui.autocomplete, {
  _renderMenu: function(ul, items) {
    var self = this, currentCategory = "";
    $.each(items, function(index, item) {
      var li;
      if (item.category != currentCategory) {
        ul.append("<li class='ui-autocomplete-category'>" + item.category + "</li>");
        currentCategory = item.category;
      }
      li = self._renderItemData(ul, item);
    });
  }
});

Working example: https://jsfiddle.net/xatu48sc/6/

I suspect there is a scope issue between the two. If this is not good and you really want to use _renderItem() then I can look into it. Using _renderItemData() does work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.