「外部からWordPressにアクセスすることができる」WP REST APIを使って、この記事では検索を作ってみます。
デフォルトで使える「WordPress検索の代替え」にも使えるので、最後までご覧いただけると嬉しいです。
目次
WP REST API
WP REST APIは、WordPressの外部からそのWordPressにアクセスすることができるAPIです。
この記事ではそんなWP REST APIで記事情報を取得するときに、カテゴリーの分類のパラメータを加えて記事を取得しています。
この辺りは、この記事の後半で詳細を解説しますがはじめに概要だけ抑えておきたい方は、以下の記事もチェックしてみてください。
WP REST APIで記事一覧を表示非同期の検索結果とは?
この記事では、APIを取得して非同期で検索結果を表示します。
WordPressのデフォルトで使う検索は、同期(ページの読み込みあり)して検索結果を表示します。
ページ遷移を挟まない非同期の検索です。
非同期で検索結果を表示するサンプル
早速、サンプルです。
以下の検索ボックスに任意のキーワードを入力し、検索ボタンを押すことで検索結果が表示されます。
上記のサンプルでは、こちらの転職サイトを対象にした検索を行わせています。
その為、検索キーワードに「セールス」「営業」「デザイナー」を入れて検索すると、いくつか記事が引っかかるので、入れてみてください。
実装の手順と方法
コードの解説の前に、ざっくりとした実装の手順と方法について解説します。
はじめに、HTMLを記述します。
表示させたい場所に記述しましょう。
<div id="search-box">
<input type="text" id="search-input" placeholder="キーワードを入力">
<button id="search-button">検索</button>
</div>
<div id="search-results">
<!-- 検索結果がここに表示されます -->
</div>
次に、JavaScriptのコードをページに記述します。
コードは <body>〜</body>
で、</body>
の閉じタグ(クロージングタグ)の前に記述しましょう。
// ボタンクリックでイベント開始
document.getElementById("search-button").addEventListener("click", async () => {
const searchInput = document.getElementById("search-input");
const searchResults = document.getElementById("search-results");
// 文字列の両端の空白を削除
const keyword = searchInput.value.trim();
// キーワードなしの場合無反応
if (keyword.length === 0) {
return;
}
const targetSiteUrl = 'https://jobtips.net/'; // APIで取得したいURLを記述
// URLをREST APIに変換
const apiUrl = `${targetSiteUrl}/wp-json/wp/v2/posts?search=${encodeURIComponent(keyword)}&_fields=id,title,link,date,featured_media,categories`;
// 画像が取得できた場合
async function getFeaturedImageUrl(mediaId) {
if (!mediaId) {
return "";
}
const mediaUrl = `${targetSiteUrl}/wp-json/wp/v2/media/${mediaId}?_fields=source_url`;
try {
const response = await fetch(mediaUrl);
const media = await response.json();
return media.source_url;
} catch (error) {
console.error("Error fetching featured image:", error);
return "";
}
}
// カテゴリーの取得
async function getCategoryName(categoryId) {
if (!categoryId) {
return "";
}
const categoryUrl = `${targetSiteUrl}/wp-json/wp/v2/categories/${categoryId}?_fields=name`;
try {
const response = await fetch(categoryUrl);
const category = await response.json();
return category.name;
} catch (error) {
console.error("Error fetching category name:", error);
return "";
}
}
// 検索開始の反応
try {
searchResults.innerHTML = "<span class='circle'></span>"; // 検索中表示するHTML
const response = await fetch(apiUrl);
const results = await response.json();
// 検索結果でヒットした場合
if (results.length > 0) {
let searchResultsHTML = "";
for (const result of results) {
const featuredImageUrl = await getFeaturedImageUrl(result.featured_media);
const date = new Date(result.date).toLocaleDateString();
const categoryName = result.categories.length > 0 ? await getCategoryName(result.categories[0]) : "";
searchResultsHTML += `
<div class="search-result-item">
<a href="${result.link}" target="_blank">
${featuredImageUrl ? `<img src="${featuredImageUrl}" src="${featuredImageUrl}" alt="${result.title.rendered}">` : ""}
<span class="search-result-category">${categoryName}</span>
<div class="search-result-textblock">
<time class="search-result-date dfont">${date}</time>
<h3 class="search-result-title">${result.title.rendered}</h3>
</div>
</a>
</div>
`;
}
searchResults.innerHTML = searchResultsHTML;
} else {
searchResults.innerHTML = "該当する記事が見つかりませんでした。";
}
} catch (error) {
searchResults.innerHTML = "検索中にエラーが発生しました。";
}
});
最後にCSSを記述して、デザインを整えます。
#search-box {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
input#search-input {
width: 86%;
margin-bottom: 0;
border: solid 1px #ccc;
border-right: none;
border-radius: 3px 0 0 3px;
}
#search-box button#search-button {
border: solid 1px #ccc;
filter: none;
margin: 0;
border-radius: 0 3px 3px 0;
border-left: none;
padding: 9px 25px;
}
#search-box button#search-button:hover {
opacity: 0.8;
}
.circle {
display: block;
width: 34px;
height: 34px;
position: relative;
background: transparent;
box-sizing: border-box;
border-top: 4px solid #f0db40;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid transparent;
border-radius: 100%;
animation: spin 0.6s ease-out infinite;
margin: 0 auto 20px;
}
@keyframes spin {
100% {transform: rotate(360deg)}
}
#search-results {
display: flex;
flex-wrap: wrap;
gap: 40px 20px;
justify-content: space-between;
}
.search-result-item {
width: 47%;
}
.search-result-item a {
display: flex;
flex-direction: column;
border-radius: 2px;
background: #fff;
box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.12), 0 2px 3px 0 rgba(0, 0, 0, 0.22);
cursor: pointer;
transition: 0.2s ease-in-out;
position: relative;
text-decoration: none;
height: 100%;
}
.search-result-item a:hover {
box-shadow: 0 15px 30px -5px rgba(0, 0, 0, 0.15), 0 0 5px rgba(0, 0, 0, 0.1);
transform: translateY(-4px);
}
span.search-result-category {
position: absolute;
top: 0;
left: 0;
background: #6bb6ff;
color: #fff;
font-size: 0.7rem;
padding: 2px 12px 1px;
font-weight: 600;
}
.search-result-textblock {
display: flex;
flex-direction: column;
gap: 0px;
padding: 15px 25px 20px;
}
time.search-result-date {
color: #b5b5b5;
font-size: 14px;
font-weight: bold;
position: relative;
}
time.search-result-date.dfont:before {
padding-right: 5px;
opacity: 0.6;
color: #b5b5b5;
font-size: 13px;
font-family: "Font Awesome 5 Free";
font-weight: 900;
content: "\f017";
}
.entry-content .search-result-title {
margin: 0;
color: #313131;
font-size: 1.2rem;
padding: 0;
}
.entry-content h3.search-result-title:before {
content: none;
}
これで完成です!
ざっくりとしたコードの解説
コードは、HTML・JavaScript・CSSの3種です。順に解説していきます。
HTML
HTMLは、検索ボックスと検索結果の表示エリアを作成して、その中にJavaScriptのコードで動的に検索機能を表示させます。
<div id="search-box">
<input type="text" id="search-input" placeholder="キーワードを入力">
<button id="search-button">検索</button>
</div>
<div id="search-results">
<!-- 検索結果がここに表示されます -->
</div>
- <div id=”search-box”>〜〜〜</div> … 検索ボックスを囲むコンテナ
- <input type=”text” id=”search-input” placeholder=”キーワードを入力”> … 検索キーワードを入力するためのテキストフィールド。placeholder属性には、キーワードを入力と設定しています。
- <button id=”search-button”>検索</button> … 検索を実行するためのボタンです。クリックすると、関連するJavaScriptのコードが実行されます。
- <div id=”search-results”> … 検索結果が表示されるコンテナです。JavaScriptのコードで、検索結果がこの要素内追加されます。
こんな感じで、HTMLは簡素な作りです。
JavaScript
JavaScriptは、「検索ボタンクリック」で検索を実行して、WP REST APIを使って結果を取得、検索結果エリアに動的に表示する非同期処理のコードです。
// ボタンクリックでイベント開始
document.getElementById("search-button").addEventListener("click", async () => {
const searchInput = document.getElementById("search-input");
const searchResults = document.getElementById("search-results");
// 文字列の両端の空白を削除
const keyword = searchInput.value.trim();
// キーワードなしの場合無反応
if (keyword.length === 0) {
return;
}
const targetSiteUrl = 'https://jobtips.net/'; // APIで取得したいURLを記述
// URLをREST APIに変換
const apiUrl = `${targetSiteUrl}/wp-json/wp/v2/posts?search=${encodeURIComponent(keyword)}&_fields=id,title,link,date,featured_media,categories`;
// 画像が取得できた場合
async function getFeaturedImageUrl(mediaId) {
if (!mediaId) {
return "";
}
const mediaUrl = `${targetSiteUrl}/wp-json/wp/v2/media/${mediaId}?_fields=source_url`;
try {
const response = await fetch(mediaUrl);
const media = await response.json();
return media.source_url;
} catch (error) {
console.error("Error fetching featured image:", error);
return "";
}
}
// カテゴリーの取得
async function getCategoryName(categoryId) {
if (!categoryId) {
return "";
}
const categoryUrl = `${targetSiteUrl}/wp-json/wp/v2/categories/${categoryId}?_fields=name`;
try {
const response = await fetch(categoryUrl);
const category = await response.json();
return category.name;
} catch (error) {
console.error("Error fetching category name:", error);
return "";
}
}
// 検索開始の反応
try {
searchResults.innerHTML = "<span class='circle'></span>"; // 検索中表示するHTML
const response = await fetch(apiUrl);
const results = await response.json();
// 検索結果でヒットした場合
if (results.length > 0) {
let searchResultsHTML = "";
for (const result of results) {
const featuredImageUrl = await getFeaturedImageUrl(result.featured_media);
const date = new Date(result.date).toLocaleDateString();
const categoryName = result.categories.length > 0 ? await getCategoryName(result.categories[0]) : "";
searchResultsHTML += `
<div class="search-result-item">
<a href="${result.link}" target="_blank">
${featuredImageUrl ? `<img src="${featuredImageUrl}" src="${featuredImageUrl}" alt="${result.title.rendered}">` : ""}
<span class="search-result-category">${categoryName}</span>
<div class="search-result-textblock">
<time class="search-result-date dfont">${date}</time>
<h3 class="search-result-title">${result.title.rendered}</h3>
</div>
</a>
</div>
`;
}
searchResults.innerHTML = searchResultsHTML;
} else {
searchResults.innerHTML = "該当する記事が見つかりませんでした。";
}
} catch (error) {
searchResults.innerHTML = "検索中にエラーが発生しました。";
}
});
細かい部分の解説は、コメントアウトの中に記載しているのでそちらをコードをと併せてご覧ください。
CSS
CSSは、検索結果のレイアウトとカードデザインを作るので、多少コード量は多めです。
#search-box {
display: flex;
justify-content: center;
align-items: center;
flex-wrap: wrap;
margin-bottom: 20px;
}
input#search-input {
width: 86%;
margin-bottom: 0;
border: solid 1px #ccc;
border-right: none;
border-radius: 3px 0 0 3px;
}
#search-box button#search-button {
border: solid 1px #ccc;
filter: none;
margin: 0;
border-radius: 0 3px 3px 0;
border-left: none;
padding: 9px 25px;
}
#search-box button#search-button:hover {
opacity: 0.8;
}
.circle {
display: block;
width: 34px;
height: 34px;
position: relative;
background: transparent;
box-sizing: border-box;
border-top: 4px solid #f0db40;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-bottom: 4px solid transparent;
border-radius: 100%;
animation: spin 0.6s ease-out infinite;
margin: 0 auto 20px;
}
@keyframes spin {
100% {transform: rotate(360deg)}
}
#search-results {
display: flex;
flex-wrap: wrap;
gap: 40px 20px;
justify-content: space-between;
}
.search-result-item {
width: 47%;
}
.search-result-item a {
display: flex;
flex-direction: column;
border-radius: 2px;
background: #fff;
box-shadow: 0 0 3px 0 rgba(0, 0, 0, 0.12), 0 2px 3px 0 rgba(0, 0, 0, 0.22);
cursor: pointer;
transition: 0.2s ease-in-out;
position: relative;
text-decoration: none;
height: 100%;
}
.search-result-item a:hover {
box-shadow: 0 15px 30px -5px rgba(0, 0, 0, 0.15), 0 0 5px rgba(0, 0, 0, 0.1);
transform: translateY(-4px);
}
span.search-result-category {
position: absolute;
top: 0;
left: 0;
background: #6bb6ff;
color: #fff;
font-size: 0.7rem;
padding: 2px 12px 1px;
font-weight: 600;
}
.search-result-textblock {
display: flex;
flex-direction: column;
gap: 0px;
padding: 15px 25px 20px;
}
time.search-result-date {
color: #b5b5b5;
font-size: 14px;
font-weight: bold;
position: relative;
}
time.search-result-date.dfont:before {
padding-right: 5px;
opacity: 0.6;
color: #b5b5b5;
font-size: 13px;
font-family: "Font Awesome 5 Free";
font-weight: 900;
content: "\f017";
}
.entry-content .search-result-title {
margin: 0;
color: #313131;
font-size: 1.2rem;
padding: 0;
}
.entry-content h3.search-result-title:before {
content: none;
}
この辺りは、サイトに応じたデザインがあると思うので、好みに応じて利用ください。
さいごに
久方ぶりのREST APIの内容で、ところどころ忘れている部分がありました。
それはさておき、WPI REST APIにはいろんなデータが入っており、検索以外でも活用できるので是非参考にして使ってみてください。