某プログラミングスクールの最終課題において、カテゴリープルダウン機能(トップページのヘッダーにあるカテゴリーという文字に触れるとプルダウンでカテゴリーが表示される機能)を実装したので、自分のアウトプットのためにも解説していきたいと思います。
早速ですが、プルダウン機能実装の流れは以下の通りとなります。
① カテゴリーの文字に触れる
② jQueryでイベントが発生し、カテゴリーに貼られているリンクを読み取る
③ そのリンクでAjax通信を行い、取得したカテゴリーID(0の場合は親要素)に応じて子要素をデータベースから取得し、json形式で値を戻す。
④ 後は、取得したデータをHTMLの形に組み直して、カテゴリーの文字の下に付け加えるだけです。
⑤ 子要素に対しても、以上と同様な流れで表示し、孫要素までいくと表示されないようにしています。
これを実現したソースコードは以下の通りとなります。(該当する部分だけ抜き出しています)
_header.html.haml
.header-bottom-left-category
.header-bottom-left-category-title
= link_to categories_path ,class:"category_name", data:{category_id:0} do
%i.fas.fa-list
カテゴリから探す
.header-bottom-left-category-field
categories_pulldown.js
$(function(){
// プルダウンのHTMLを生成
function buildHTML(categories){
var link_tag, outline = $("<ul>");
var link_class =$(".header-bottom-left-category-title").find("a").attr("class")
var base_href = $(".header-bottom-left-category-title").find("a").attr("href") + "/";
categories.forEach(function(category){
link_tag = $("<a>", { href: base_href + category.id ,"class":link_class }).text(category.name)
link_tag = $("<li>").append(link_tag)
outline.append(link_tag)
})
return outline
}
// カーソルが触れたときに起動
$(document).on({
// カーソルが乗ったときに起動
'mouseenter' : function(enter_event) {
// mouseoverのデフォルト動作をクリアする
enter_event.preventDefault();
// カテゴリーのIDを取得できた場合には、子要素を取得
$.ajax({
url: $(this).attr("href"),
type: "GET",
dataType: "json",
context: this,
cache: false
})
.done(function(categories){
// 子要素がなければ選択肢を表示しない
if( categories.length > 0 ){
// 「カテゴリから探す」以外に触れたら一掃する
if( $(this).data("category-id") == 0){
$(".header-bottom-left-category-field").empty();
}
// それ以外の場合は自分の後に表示されている子要素を削除する
else{
$(this).closest("ul").nextAll().remove()
}
// 選択したフォームの下に新たなフォームを追加
var html = buildHTML(categories)
$(".header-bottom-left-category-field").append(html)
}
})
.fail(function(error){
alert(error)
})
},
// カーソルが離れたときに起動
'mouseleave' : function(leave_event){
// mouseleaveのデフォルト動作をクリアする
leave_event.preventDefault();
// 出る前と後の親要素のインデックスを取得する。
var old_parent = $(this).closest("ul").index();
var new_parent = $(leave_event.relatedTarget).closest("ul").index();
// 子要素へ移る場合はオレンジ色のままにする
if( old_parent < new_parent ){
$(this).css("color","orange");
}
// 親要素に移る場合には、その先の要素全てを黒にする。
else if(old_parent > new_parent ){
$(leave_event.relatedTarget).closest("ul").find("a").css("color","");
}
}
},".category_name");
// カテゴリーリストからカーソルが離れたらすべて消す
$(".header-bottom-left-category").mouseleave(function(){
$(".header-bottom-left-category-field").empty();
$(".header-bottom-left-category-title").find("a").css("color","");
})
})
routes.rb
resources :categories, only: [:index,:show]
categories_controller.rb
class CategoriesController < ApplicationController
def index
@categories = Category.where(ancestry:nil)
respond_to do |format|
format.html
format.json{
render json: @categories
}
end
end
def show
@category = Category.find(params[:id])
@categories = @category.children
respond_to do |format|
format.html
format.json{
render json: @categories
}
end
end
end
_header.scss
& a {
color:black;
padding:10px;
display: block;
text-decoration: none;
&:hover {
color:orange;
}
}
> div {
display: flex;
}
&-left{
&-category {
position: relative;
&-field {
position: absolute;
display: flex;
background-color:white;
z-index:1;
& ul {
width:200px;
}
}
}
}