I’m trying to develop a tabbed search results page using the Google Programmable Search Engine (PSE) and their JSON API in order to have the same appearance as Google’s hosted search engine, which shows search and images results in two tabs, i.e. like this sample engine https://cse.google.com/cse?cx=b67b93cd338f948e5
The hosted search, above, shows the results for the search term in both the Web and the Image tabs. That’s what I want to do with a search based on the JSON API.
I’m using this answer as a starting point for using the JSON API, Is there a working sample of the Google custom search rest API? and it works.
With the JSON API, I can show search results with the thumbnail and snippet, or only images, using the image only search.
My question: How do I show results from both types of searches at the same time in their respective tabs? Like the hosted search?
The problem I’m having is the hndlr(response)
which appends the script to the document. How do I add two hndlrs
? Or is that the wrong approach?
The hndlr
JSON response shows the thumbnail and snippet with a URL like this:
https://www.googleapis.com/customsearch/v1?key=KEY&cx=CX&q=test&callback=hndlr
And this URL shows image only results when &searchType=image is appended to the URL string, like this:
https://www.googleapis.com/customsearch/v1?key=KEY&cx=CX&q=test&callback=hndlr&searchType=image
One problem is that the JSON response.items
are not the same between the standard search and the image only search, so I can’t simply append &searchType=image
to the URL.
Do I need to run two complete hndlr
loops? But if so, how do I append each to the document body without conflicts and to display in each tab?
So how can I get both thumbnail and snippet results in the Search Results tab and image only results in the Image Results tab at the same time and for the same query?
I can’t do a JSFiddle, because the JSON API keys are limited to 100 queries, and the code references a URL, which won’t work in a Fiddle.
(CSS and some Javascript response.items omitted for clarity. Both HTML and Javascript are in the same index.html file)
index.html:
<html>
<head>
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</head>
<body>
<form action="searchresults.html" id="cse-search-box">
<div>
<input class="" name="q" type="text">
<input class="" type="submit">
</div>
</form>
<ul class="nav nav-tabs" id="mytabs" role="tablist">
<li class="nav-item" role="presentation">
<a class="nav-link active" id="tab-0" data-bs-toggle="tab" href="#tabpanel-0" role="tab" aria-controls="tabpanel-0" aria-selected="true">Search Results</a>
</li>
<li class="nav-item" role="presentation">
<a class="nav-link" id="tab-1" data-bs-toggle="tab" href="#tabpanel-1" role="tab" aria-controls="tabpanel-1" aria-selected="false">Image Results</a>
</li>
</ul>
<div class="tab-content pt-5" id="tab-content">
<div class="tab-pane active" id="tabpanel-0" role="tabpanel" aria-labelledby="tab-0">
<div class="gsc-result-info" id="resultmeta"></div>
<div id="searchresults"></div>
</div>
<div class="tab-pane" id="tabpanel-1" role="tabpanel" aria-labelledby="tab-1">
<div class="gsc-result-info" id="resultmeta"></div>
<div id="images"></div></div>
</div>
<script> (Javascript below)
Javascript:
function hndlr(response) {
//Search results load time
document.getElementById("resultmeta").innerHTML = "About " + response.searchInformation.formattedTotalResults + " results (" + response.searchInformation.formattedSearchTime + " seconds)";
// Clear the divs first
document.getElementById("searchresults").innerHTML = "";
document.getElementById("images").innerHTML = "";
// Loop through each item in search results
for (var i = 0; i < response.items.length; i++) {
var item = response.items[i];
var content = "";
var imagesonly = "";
// Search Results output simplified for clarity
// Title Link
content += "<a class='gs-title' href='" + item.link + "'>" + item.htmlTitle + "</a><br />";
// Thumbnail image for search results
content += "<a href='" + item.link + "'><img src='" + item.pagemap.cse_thumbnail[0].src + "'></a>";
}
// Pagination
var totalPages = Math.ceil(response.searchInformation.totalResults / 10);
console.log(totalPages);
var currentPage = Math.floor(start / 10 + 1);
console.log(currentPage);
var pageControls = "<div class='gsc-results'><div class='gsc-cursor-box gs-bidi-start-align' dir='ltr'><div class='gsc-cursor'>";
//Page change controls, 10 max.
for (var x = 1; x <= totalPages && x<=10; x++) {
pageControls += "<div class='gsc-cursor-page";
if (x === currentPage)
pageControls += " gsc-cursor-current-page";
var pageLinkStart = x * 10 - 9;
pageControls+="'><a href='test.html?start="+pageLinkStart+"&q="+query+"'>"+x+"</a></div>";
}
pageControls += "</div></div></div>";
document.getElementById("searchresults").innerHTML += pageControls;
document.getElementById("images").innerHTML += pageControls;
}
//Get search text from query string.
var query = document.URL.substr(document.URL.indexOf("q=") + 2);
var start = document.URL.substr(document.URL.indexOf("start=") + 6, 2);
if (start === "1&" || document.URL.indexOf("start=") === -1)
start = 1;
//Load the script src dynamically to load script with query to call.
// DOM: Create the script element
var gsearchresults = document.createElement("script");
// set the type attribute
gsearchresults.type = "application/javascript";
// make the script element load file
gsearchresults.src = "https://www.googleapis.com/customsearch/v1?key=KEY&cx=CX&start="+start+"&q=" +query +"&callback=hndlr";
// finally insert the element to the body element in order to load the script
document.body.appendChild(gsearchresults);
</script>
</body>
</html>
2
Answers
You have to make two separate requests to the API.
This means using two variables to hold the different responses (for each search type).
//# each request seems to get always
json.txt
but content is different//# depends if
&searchtype=image
is used, etc…PS: Will show testable demo soon (dealing with CORS issues on my example live demo)
There’s probably a cleaner and more efficient way to do this, but this works by running two API queries and two item loops, and changing the name of the function and variable so they don’t collide. You’ll need to add your HTML markup in each loop, but the basics are here. Load Bootstrap CSS and Javascript as needed.
test.html