Ad/iklan :







Search.Pencarian Menu

Add text send email to rh3252705.adda@blogger.com or Click this (Text porn Will delete) | Tambah teks kirim email ke rh3252705.adda@blogger.com atau Klik ini (Teks porno akan dihapus)
Total post.pos : 13631+

[Go Make Things] How to create your own search API for a static website with JavaScript and PHP (part 3)

On Tuesday, I shared how I created my own search API for my static website with PHP. And yesterday, I walked you through how I built a custom web component to call the API and render search results.

Today, I wanted to wrap up the series by sharing how I added two features:

  1. Filtering search results by type.
  2. Custom URLs for search queries and bookmarking.

Let's dig in!

Filtering search results by type

For this feature, I first added a createFilterHTML() method.

It accepts an object of types that exist in the search results, and how many results are that type. It might look like this…

let types = {  	Articles: 42,  	Courses: 3,  	Toolkit: 7  };  

Inside my function use the Object.keys() method to get the keys from the types object. If there are none, I return an empty string ('').

Otherwise, I return an HTML string with a collection of checkbox inputs, one for each type. I have mine styled as an inline list, but you can style them however you want.

Each input has a name property of search-filter, it's value is the type, and it's checked by default.

/**   * Create the filter HTML   * @param  {Object} types The content types   * @return {String}       The HTML string   */  createFilterHTML (types) {  	let keys = Object.keys(types);  	if (keys.length < 1) return '';  	return `  		<fieldset>  			<legend>Filter results by type</legend>    			${keys.sort().map(function (type) {  				let count = types[type];  				return `  					<label>  						<input type="checkbox" name="search-filter" value="${type}" checked>${type} (${count})  					</label>`;  			}).join('')}    		</fieldset>`;  }  

Next, I updated my createResultsHTML() function.

First, I created a types object ({}) to hold my type data. Instead of immediately returning my HTML string, I assign it to a variable.

Inside the Array.prototype.map() callback function, I either add the article.type to my types object with a value of 1, or increase the type count by 1. This is how I track how many of each type are in the results.

Finally, I pass my types object into the createFilterHTML() method, and return the resulting string and my html string for the results.

/**   * Create the markup for results   * @param  {Array}  results The results to display   * @return {String}         The results HTML   */  createResultsHTML (results) {  	let types = {};  	let html = results.map(function (article) {  		types[article.type] = types[article.type] ? types[article.type] + 1 : 1;  		return `  			<div>  				<aside>  					<strong>${article.type}</strong> - <time datetime="${article.datetime}" pubdate>${article.date}</time>  				</aside>  				<h2>  					<a href="${article.url}">${article.title}</a>  				</h2>  				${article.summary.slice(0, 150)}...  			</div>`;  	}).join('');  	return this.createFilterHTML(types) + html;  }  

Now, the filters are displayed in the UI.

Hiding and showing search results when filters change

Next, I updated my handleEvent() method. Instead of just running my search code, I dynamically run an on* method on my Web Component instance.

For example, a submit event would trigger the onsubmit() method to run.

/**   * Handle events   */  handleEvent (event) {  	this[`on${event.type}`](event);  }  

Then, I copy/pasted the code that was previously in the handleEvent() method into an onsubmit() method.

/**   * Handle submit events   */  onsubmit (event) {  	event.preventDefault();  	this.search(new FormData(this.form));  }  

Back in my createResultsHTML() method, I added a [data-search-type] attribute to each search result, with the article.type as its value.

return `  	<div data-search-type="${article.type}">  		...  	</div>`;  

Then, I created an oninput() event to handle changes to my filters.

In this method, I use the Element.querySelectorAll() method to get all of the [name="search-filter"] items that are :checked. Then I use the Array.from(), Array.prototype.map(), and Array.prototype.join() methods to create a [data-search-type="*"] selector string, where * is the type of item that should be visible but is currently [hidden].

/**   * Handle checkbox changes   */  oninput (event) {    	// Get the values to show and hide  	let show = Array.from(this.results.querySelectorAll('[name="search-filter"]:checked')).map(input => `[data-search-type="${input.value}"][hidden]`).join(',');    }  

For example, let's say Articles and Toolkit are checked, but Courses is not.

The resulting show string would look like this…

let show = '[data-search-type="Articles"][hidden], [data-search-type="Toolkit"][hidden]';  

I repeat this process, this time looking for filters that are :not(:checked), and getting the matching search results that are :not([hidden]).

/**   * Handle checkbox changes   */  oninput (event) {    	// Get the values to show and hide  	let show = Array.from(this.results.querySelectorAll('[name="search-filter"]:checked')).map(input => `[data-search-type="${input.value}"][hidden]`).join(',');  	let hide = Array.from(this.results.querySelectorAll('[name="search-filter"]:not(:checked)')).map(input => `[data-search-type="${input.value}"]:not([hidden])`).join(',');  }  

If there are items to show, I pass the select into the Element.querySelectorAll() method. Then, I use a for...of loop to loop through them, and the Element.removeAttribute() method to remove the [hidden] attribute.

I do the same thing for items that I should hide, this time adding the [hidden] attribute with the Element.setAttribute() method.

// Show hidden elements  if (show) {  	for (let elem of this.results.querySelectorAll(show)) {  		elem.removeAttribute('hidden');  	}  }    // Hide visible elements  if (hide) {  	for (let elem of this.results.querySelectorAll(hide)) {  		elem.setAttribute('hidden', '');  	}  }  

One final touch to make this work: in my constructor(), I add an input listener on the this.results element.

// Listen for events  this.form.addEventListener('submit', this);  this.results.addEventListener('input', this);  

Updating the URL

Inside the search() function, I use the history.pushState() method to update the URL without reloading the page.

I pass in an empty object for the state, though if your app actually uses browser state, you could pass in history.state instead. I pass in an empty string for the deprecated second argument, and the current URL with ?s and the query value as the query string parameter.

/**   * Search the API   * @param  {FormData} query The form data to search form   */  async search (query) {  	try {    		// Show status message  		this.notify.innerHTML = '<p<em>Searching...</em></p>';  		this.results.innerHTML = '';    		// Update the URL  		history.pushState({}, '', window.location.origin + window.location.pathname + '?s=' + query.get('q'));    		// ...    	} catch (error) {}    }  

Now, I have a URL query string parameter I can check for when the page loads to automatically run a search.

I created one last method, onload(). In it, I use the new URLSearchParams() object to get the value of the s query string from the window.location.search property.

If no query exists, I can return to end early.

/**   * If there's a query string search term, search it on page load   */  onload () {  	let query = new URLSearchParams(window.location.search).get('s');  	if (!query) return;  }  

Otherwise, I create a new FormData() object, and assign my query to the q property (the same as if someone had typed it into the form).

I pass the formData into the search() method to kick off a call to the search API. Then, I get the search input field and update its value to the query.

/**   * If there's a query string search term, search it on page load   */  onload () {  	let query = new URLSearchParams(window.location.search).get('s');  	if (!query) return;  	let formData = new FormData();  	formData.set('q', query);  	this.search(formData);  	let input = this.form.querySelector('[name="q"]');  	input.value = query;  }  

Now, when someone visits a search page they had bookmarked, the site automatically displays the saved search query results.

Want to build cool stuff like this?

I can help!

I offer consulting services to help developers and developer teams write code that's faster, simpler, and easier to maintain.

I also teach developers how to build a simpler web through courses and workshops.

Feel free to reach out with any questions or comments.

Cheers,
Chris

Want to share this with others or read it later? View it in a browser.

Share :

Facebook Twitter Google+
0 Komentar untuk "[Go Make Things] How to create your own search API for a static website with JavaScript and PHP (part 3)"

Back To Top