質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.46%
CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

Q&A

解決済

4回答

3023閲覧

アコーディオンを最初から開いた状態とアコーディオンを1つ開いたら、他が閉じる状態を作りたい。

noel0718

総合スコア20

CSS3

CSS(Cascading Style Sheet)の第3版です。CSS3と略されることが多いです。色やデザインを柔軟に変更することが可能になります。

HTML5

HTML5 (Hyper Text Markup Language、バージョン 5)は、マークアップ言語であるHTMLの第5版です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

jQuery

jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

CSS

CSSはXMLやHTMLで表現した色・レイアウト・フォントなどの要素を指示する仕様の1つです。

0グッド

0クリップ

投稿2021/09/19 09:32

編集2021/09/30 12:52

アコーディオンの処理で書いたjavascriptを流用して、以下パターンを作りたいです。

■アコーディオンを最初から開いた状態
htmlにis-activeというクラスをつけて、
最初から開いた状態にしたいです。

■アコーディオンを1つ開いたら、他が閉じる状態になる
htmlに--oneというクラスをつけて、
1つ開いたら、他が閉じる状態にしたいです。

今javascriptでできているの処理は通常のアコーディオンになります。
アコーディオンの中にアコーディオンがあるパターンやtriggerとpanelが同階層にない場合も想定してうまく動いています。
よくあるslideDownやslideUpのやり方ではなくmax-heightを使ったアコーディオンの処理としています。

通常のアコーディオンとバッティングしてしまい、
処理の分岐をどうしていいかわからなくなりました。

もし書き方がわかる方がいましたら、教えていただけますと幸いです。

通常のアコーディオンのHTML

html

1<div class="c-accordion js-accordion"> 2 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 3 <span class="c-headline-lv2">アコーディオンタイトル</span> 4 </button> 5 <div class="c-accordion__panel js-accordion__panel"> 6 <div class="c-accordion__content"> 7 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 8 </div> 9 </div> 10</div> 11<div class="c-accordion js-accordion"> 12 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 13 <span>アコーディオンタイトル</span> 14 </button> 15 <div class="c-accordion__panel js-accordion__panel"> 16 <div class="c-accordion__content"> 17 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 18 </div> 19 <!--アコーディオンの中にアコーディオンがある場合--> 20 <div class="c-accordion js-accordion"> 21 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 22 <span>アコーディオンタイトル</span> 23 </button> 24 <div class="c-accordion__panel js-accordion__panel"> 25 <div class="c-accordion__content"> 26 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 27 </div> 28 </div> 29 </div> 30 </div> 31</div>

最初から開いた状態のアコーディオンのHTML

html

1<div class="c-accordion js-accordion is-active"> 2 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 3 <span class="c-headline-lv2">アコーディオンタイトル</span> 4 </button> 5 <div class="c-accordion__panel js-accordion__panel"> 6 <div class="c-accordion__content"> 7 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 8 </div> 9 </div> 10</div> 11<div class="c-accordion js-accordion"> 12 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 13 <span>アコーディオンタイトル</span> 14 </button> 15 <div class="c-accordion__panel js-accordion__panel"> 16 <div class="c-accordion__content"> 17 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 18 </div> 19 <!--アコーディオンの中にアコーディオンがある場合--> 20 <div class="c-accordion js-accordion"> 21 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 22 <span>アコーディオンタイトル</span> 23 </button> 24 <div class="c-accordion__panel js-accordion__panel"> 25 <div class="c-accordion__content"> 26 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 27 </div> 28 </div> 29 </div> 30 </div> 31</div>

1つ開いたら、他が閉じる状態のアコーディオンのHTML

html

1<div class="c-accordion js-accordion --one"> 2 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 3 <span class="c-headline-lv2">アコーディオンタイトル</span> 4 </button> 5 <div class="c-accordion__panel js-accordion__panel"> 6 <div class="c-accordion__content"> 7 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 8 </div> 9 </div> 10</div> 11<div class="c-accordion js-accordion --one"> 12 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 13 <span>アコーディオンタイトル</span> 14 </button> 15 <div class="c-accordion__panel js-accordion__panel"> 16 <div class="c-accordion__content"> 17 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 18 </div> 19 <!--アコーディオンの中にアコーディオンがある場合--> 20 <div class="c-accordion js-accordion"> 21 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 22 <span>アコーディオンタイトル</span> 23 </button> 24 <div class="c-accordion__panel js-accordion__panel"> 25 <div class="c-accordion__content"> 26 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 27 </div> 28 </div> 29 </div> 30 </div> 31</div>

css

1 2[class*="js-accordion"]:not([data-accordion-device="sp"]) .js-accordion__trigger { 3 cursor: pointer; 4} 5 6[class*="js-accordion"]:not([data-accordion-device="sp"]).is-hidden .js-accordion__panel { 7 position: absolute; 8 opacity: 0; 9 visibility: hidden; 10} 11 12[class*="js-accordion"]:not([data-accordion-device="sp"]) .js-accordion__panel { 13 overflow: hidden; 14 transition: 0.5s max-height; 15 will-change: max-height; 16} 17 18.c-accordion { 19 border: #ddd 1px solid; 20} 21 22.c-accordion + .c-accordion { 23 margin-top: -1px; 24} 25 26.c-accordion__trigger { 27 position: relative; 28 width: 100%; 29 padding: 15px 60px 15px 20px; 30 font-weight: bold; 31 background-color: #555; 32 color: #fefefe; 33} 34 35.c-accordion__content { 36 padding: 20px; 37} 38

####追記1

1点、実装中にうまくいかないことがありました。
スマホの際、ヘッダーのメニュー内にアコーディオンがあります。メニューを開いた後にアコーディオンを開き、メニューを閉じるとアコーディオンも開じるようにしたいです。開いたままですと、メニュー内の文字のアニメーションができなくなることが判明しました。

メニューを閉じた際にアコーディオンも閉じる処理を無理矢理書いたのですが、アコーディオンのjavascriptでの判定ですと閉じていないため、再度メニューを開いた際に2度クリックしないとアコーディオンが開かないのが現状となります。

html

1<header class="l-header" role="banner"> 2 <div class="l-header__inner"> 3 <div class="l-header__menu"> 4 <div class="l-header__menu-inner"> 5 <nav class="l-header__menu-nav"> 6 <ul class="l-header__menu-nav-list"> 7 <li class="l-header__menu-nav-item"> 8 <div class="c-accordion js-accordion"> 9 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 10 <span class="c-headline-lv2">アコーディオンタイトル</span> 11 </button> 12 <div class="c-accordion__panel js-accordion__panel"> 13 <div class="c-accordion__content"> 14 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 15 </div> 16 </div> 17 </div> 18 <div class="c-accordion js-accordion"> 19 <button type="button" class="c-accordion__trigger js-accordion__trigger"> 20 <span class="c-headline-lv2">アコーディオンタイトル</span> 21 </button> 22 <div class="c-accordion__panel js-accordion__panel"> 23 <div class="c-accordion__content"> 24 <p>この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。この文章はダミーです。文字の大きさ、量、字間、行間等を確認するために入れています。</p> 25 </div> 26 </div> 27 </div> 28 </li> 29 </ul> 30 </nav> 31 </div> 32 </div> 33 <button type="button" class="l-header__hamburger"> 34 <span class="l-header__hamburger-inner"> 35 <span class="l-header__hamburger-bars"> 36 <span class="l-header__hamburger-bar"></span> 37 <span class="l-header__hamburger-bar"></span> 38 <span class="l-header__hamburger-bar"></span> 39 </span> 40 </span> 41 </button> 42 </div> 43</header>

javascript

1$(function () { 2 menu.onInit(); 3}); 4 5// ---------------------------------------- 6// メニュー 7// ---------------------------------------- 8const menu = { 9 onInit: function onInit() { 10 this.__Flag_open = false; 11 this.$menu = $(".l-header__menu"); 12 this.$button = $(".l-header__hamburger"); 13 $(document).on({ 14 click() { 15 if (!menu.__Flag_open) { 16 menu.open(); 17 } else { 18 menu.close(); 19 } 20 }, 21 }, '.l-header__hamburger'); 22 $(document).on({ 23 click() { 24 menu.close(); 25 }, 26 }, '.l-header__menu a[href]'); 27 }, 28 open: function open() { 29 const _this = this; 30 _this.__Flag_open = true; 31 }, 32 close: function close() { 33 const _this = this; 34 _this.__Flag_open = false; 35 //うまくいかない記述 36 $('.l-header__menu .js-accordion').removeClass('is-active'); 37 $('.l-header__menu .js-accordion').addClass('is-hidden'); 38 $('.l-header__menu .js-accordion__trigger').attr("aria-expanded", false); 39 $('.l-header__menu .js-accordion__panel').attr("aria-hidden", true); 40 $('.l-header__menu .js-accordion__panel').css({ 41 maxHeight: "", 42 }); 43 } 44} 45 46class Accordion { 47}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答4

0

最終的なソースは以下のようになりました。

javascript

1class Accordion { 2 static instancesMap = new Map; 3 static closeAll() { 4 Array.from(Accordion.instancesMap.values()).forEach(acc => acc.close()); 5 } 6 constructor(element) { 7 Accordion.instancesMap.set(element, this); 8 this.element = element; 9 this.tabs = this.element.querySelector('.js-accordion__trigger'); 10 this.panels = this.element.querySelector('.js-accordion__panel'); 11 this.siblings = Array.from(this.element.parentNode.childNodes).filter( 12 node => node !== this.element && node.classList && node.classList.contains('js-accordion') 13 ); 14 this.isExclusive = this.element.classList.contains("--one"); 15 this.isOpen = false; 16 this.height = 0; 17 this.events(); 18 this.init(); 19 } 20 events() { 21 this.tabs.addEventListener('click', this.handleClick.bind(this)); 22 this.panels.addEventListener('transitionend', this.handleTransition.bind(this)); 23 } 24 init() { 25 if (!this.element.classList.contains('is-active')){ 26 this.close(); 27 }else{ 28 this.open(); 29 this.height = this.panels.scrollHeight; 30 } 31 } 32 handleClick() { 33 if (this.isOpen) { 34 this.close(); 35 } else { 36 this.open(); 37 this.height = this.panels.scrollHeight; 38 } 39 } 40 open() { 41 this.isOpen = true; 42 this.element.classList.add('is-active'); 43 this.element.classList.remove('is-hidden'); 44 this.panels.style.maxHeight = `${0}px`; 45 this.tabs.setAttribute('aria-expanded', "true"); 46 this.panels.setAttribute('aria-hidden', "false"); 47 setTimeout(() => { 48 this.panels.style.maxHeight = `${this.height}px`; 49 }, 1); 50 if (this.element.classList.contains("--one")){ 51 this.siblings.forEach(elm => { 52 const sibling = Accordion.instancesMap.get(elm); 53 if (sibling && sibling.isOpen) sibling.close(); 54 }); 55 } 56 this.siblings.forEach(elm => { 57 const sibling = Accordion.instancesMap.get(elm); 58 if (sibling && sibling.isOpen && (this.isExclusive || sibling.isExclusive)) { 59 sibling.close(); 60 } 61 }); 62 } 63 close() { 64 this.isOpen = false; 65 this.panels.style.maxHeight = `${this.height}px`; 66 this.tabs.setAttribute('aria-expanded', "false"); 67 this.panels.setAttribute('aria-hidden', "true"); 68 setTimeout(() => { 69 this.panels.style.maxHeight = `${0}px`; 70 }, 1); 71 } 72 handleTransition() { 73 if (!this.isOpen) { 74 this.element.classList.add('is-hidden'); 75 this.element.classList.remove('is-active'); 76 } 77 this.panels.style.maxHeight = ''; 78 } 79} 80Array.from(document.querySelectorAll('.js-accordion')).forEach((el) => { 81 new Accordion(el, { 82 tabs: this.tabs, 83 panels: this.panels 84 }); 85});

投稿2021/10/01 12:16

noel0718

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

なるほどのう

通常アコーディオンゆうたら、これみたいに一次元のリストやけど、noel0718はんの作ろうゆうもんは、質問にあるHTMLの中にも

<!--アコーディオンの中にアコーディオンがある場合-->

とあるように入れ子がある、ゆうたらツリーやんな。そやからちょっと工夫いるやろな。
なんや、自己解決っぽい回答に

アコーディオンを最初から開いた状態は以下のようにしたら、できました。

とあったので、この回答では、開いているのをひとつだけにする方のみについて回答するわな。

ほんで、要件の確認なんやけど、

■アコーディオンを1つ開いたら、他が閉じる状態になる

ようにしたいということなんやけど、その、開いているのは(最大で)1個というのは、アコーディオンのツリーの中で、同じ親をもつ、兄弟同士の中で最大1個という解釈でよろしいかのう? この解釈でええのやったら、質問本文にある class Accordion に対して、少し対症療法的な修正になってまうのやけど、以下のようなコードを追加してみてはどうやろ?

1. class Accordion 冒頭部分

diff

1 class Accordion { 2+ static instancesMap = new Map; 3+ 4 constructor(element) { 5+ Accordion.instancesMap.set(element, this); 6 this.element = element; 7 this.tabs = this.element.querySelector('.js-accordion__trigger'); 8 this.panels = this.element.querySelector('.js-accordion__panel'); 9+ this.siblings = Array.from(this.element.parentNode.childNodes).filter( 10+ node => node !== this.element && node.classList && node.classList.contains('js-accordion') 11+ ); 12 this.isOpen = false;
修正点
  • staticなプロパティとして、(CSSクラスjs-accordionを持つ想定の)DOM要素をキーとし、これをもとに作成されたAccordionインスタンスとのマップinstancesMapを追加
  • コンストラクタの冒頭で、instancesMapに、引数で渡された要素とthisインスタンスのkey-valueペアを追加
  • インスタンスのプロパティ siblings を追加して、DOMツリーの中で兄弟関係にあるCSSクラスjs-accordionの要素の配列を持たせる。(自身は除外する。)

2. open() メソッド

diff

1 open() { 2 this.isOpen = true; 3 this.element.classList.add('is-active'); 4 this.element.classList.remove('is-hidden'); 5 this.panels.style.maxHeight = `${0}px`; 6 this.tabs.setAttribute('aria-expanded', "true"); 7 this.panels.setAttribute('aria-hidden', "false"); 8 setTimeout(() => { 9 this.panels.style.maxHeight = `${this.height}px`; 10 }, 1); 11+ this.siblings.forEach(elm => Accordion.instancesMap.get(elm).close()); 12 } 13 close() { 14 this.isOpen = false;
修正点
  • 兄弟関係にある他のjs-accordionクラスの要素から作成されたAccordionインスタンスの各close()メソッドを呼ぶ処理を追加

      

 

以上ですぅ。これでうまくいかへんやろか〜?


追記1

上に書いた追加するコードの中で、open() メソッドには1行、

diff

1+ this.siblings.forEach(elm => Accordion.instancesMap.get(elm).close());

を追加すると書いたけど、ちょっと大雑把ゆうかせっかちやったな。もう少し丁寧に書けば、こない

diff

1+ this.siblings.forEach(elm => { 2+ const sibling = Accordion.instancesMap.get(elm); 3+ if (sibling && sibling.isOpen) sibling.close(); 4+ });

なのを追加や。

追記2

open()メソッドに追加する、追記1の4行

diff

1+ this.siblings.forEach(elm => { 2+ const sibling = Accordion.instancesMap.get(elm); 3+ if (sibling && sibling.isOpen) sibling.close(); 4+ });

による兄弟アコーディオンを閉じる処理を、CSSクラス--oneを持っているときのみ行うようにするには、この4行を
if (this.element.classList.contains("--one")) {…}で囲んだ以下

diff

1+ if (this.element.classList.contains("--one")){ 2+ this.siblings.forEach(elm => { 3+ const sibling = Accordion.instancesMap.get(elm); 4+ if (sibling && sibling.isOpen) sibling.close(); 5+ }); 6+ }

の6行をopen() に追加やね。

追記3

もうひとつ。 上に書いた追記2 では、

  • 自身(this)が--oneを持っているときのみ、兄弟アコーディオンの中で開いているものを閉じる

ようにしているわけですが、これよりも自身を排他的に開く場合が増える条件として、

  • 自身(this)が--oneを持っているか、もしくは兄弟アコーディオンの中で開いているものが--oneを持っていれば、兄弟アコーディオンの中で開いているものを閉じる。

ようにしたいのであれば、まずコンストラクターに以下の一行

diff

1+ this.isExclusive = this.element.classList.contains("--one");

を追加する。これは排他的(exclusive)に開くアコーディオンであることを表すフラグや。これを使うて、open() には以下の6行

diff

1+ this.siblings.forEach(elm => { 2+ const sibling = Accordion.instancesMap.get(elm); 3+ if (sibling && sibling.isOpen && (this.isExclusive || sibling.isExclusive)) { 4+ sibling.close(); 5+ } 6+ });

を追加したらええと思うで。

追記4

Accordionクラスのstaticなメソッドとして、すべてのアコーディオンを閉じる closeAll()を追加する。

diff

1 class Accordion { 2 static instancesMap = new Map; 3 4+ static closeAll() { 5+ Array.from(Accordion.instancesMap.values()).forEach(acc => acc.close()); 6+ } 7+ 8 constructor(element) { 9 Accordion.instancesMap.set(element, this); 10 this.element = element;

それで、メニューを閉じる処理の中で

メニューを閉じた際にアコーディオンも閉じる処理を無理矢理書いた

コードの替わりに、アコーディオンを閉じる処理は、下記の一行

javascript

1Accordion.closeAll();

に任せればええんちゃうかな〜

投稿2021/09/29 11:19

編集2021/09/30 14:38
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

noel0718

2021/09/29 15:37

ご回答いただいた内容を元にしたところ、思った通りの動作となりました。 解説も丁寧にしていただき、神様としか言いようがありません。ありがとうございます。 ただ1点問題がありまして、 1つ開いたら、他が閉じる状態になるものはHTMLでは以下のような形を想定しております。 <div class="c-accordion js-accordion --one"> --oneというクラスがある場合のみ 1つ開いたら、他が閉じる状態になるにしたいのですが、どのようにしたらいいでしょうか。 こちらが解消できればと思います。
退会済みユーザー

退会済みユーザー

2021/09/29 23:47

おおきに〜 なんや、うまくいきそうでよかったわー > ただ1点問題がありまして、・・・ の対応策を、回答の「追記2」に書きました。参考にしたって〜
noel0718

2021/09/30 12:48

試したところうまくいきました。本当にありがとうございます。 今回の件は解決しましたが、別途実装中にうまくいかないことが出てきまして、、何度も申し訳ございませんが教えていただきたいです。 「追記1」として追記しました。解決できれば嬉しいです。
退会済みユーザー

退会済みユーザー

2021/09/30 14:39

ふむふむ。回答の「追記4」に対策を書きました。うまくいくとええけどの〜
noel0718

2021/10/01 12:10

うまくいきました。再三ありがとうございます。 数日悩んでおりましたので、大変助かりました・・。 また何かありましたら、ご相談させてください。
退会済みユーザー

退会済みユーザー

2021/10/01 16:18

おおきに〜 DOMの操作を、今回の Accordionクラスのようなヘルパー的なクラスを作って、そのインスタンスだったり、クラスのstaticプロパティやstaticメソッドを通じて、DOMの操作を行うという着想そのものは良いと思います。 それで多分、 「原則、アコーディオンの外部からはAccordionクラスやインスタンスを通じてのみ操作を行う」 といった決まりごとを設けることになると思いますが、始めのうちはその決め事でうまく行くと思います。しかし、何となくの悪い予想として、今後も機能拡張しているうちに、今回の > メニューを閉じた際にアコーディオンも閉じる処理を無理矢理書いた 的なコードをどうしても書かなければならなくなり、DOM側の状態とAccordionインスタンスとの間に不整合が生じて、それをどうにかつじつまを合わせるコードを足していくうちに、いわゆる「秘伝のタレ」化していくような気がしないでもないです。そして、そのような、JQueryの利用含むDOMの直接の操作と、Accordionクラスによる抽象化の齟齬の発生とその解消に苦労した後に、Reactをやってみるとよろしいかもしれません。 今回、アコーディオンを構成するDOM要素に並行するように、Accordionクラスのインスタンスツリーを作り、このAccordionクラスのインスタンスを通じたopen()だったりclose()だったりの抽象化されたメソッドを通じてDOMを操作しようとしているわけですが、この直接DOMを操作しないようにするためのオブジェクトのツリーをReactは仮想DOMとして実現しており、今回のAccordionクラスはある特定のCSSクラスを持つHTML要素の組み合わせに対する専用のクラスでしたが、ReactではどんなHTML要素の組み合わせに対しても対応するReactコンポーネントと呼ばれるクラス(最近のReactはクラスではなく関数で書きますが。)が、今回のAccordionオブジェクトに期待している役割を担ってくれます。
noel0718

2021/10/02 10:34

丁寧なご解説ありがとうございます。 Reactはやったことがありませんので、全くわからないのですがそういう違いがあるのですね。 勉強になります。後々Reactもやってみたいと思います。 例えばですが、閉じるとは逆で同意チェックボックスのようなものでチェックをいれたら アコーディオンが開くような場合、 if($('#submit').checked) { //アコーディオンを開く処理 $('.js-accordion[data-checkd]').addClass('is-active'); } 上記ですとうまくいかないのですが、Accordionにstaticなメソッドを追加しないといけないでしょうか。
退会済みユーザー

退会済みユーザー

2021/10/03 07:03

はい。今の作りだと、 > Accordionにstaticなメソッドを追加 して対応することになるかと思います。具体的には、 Accordion. instancesMap のキーとして登録されているdiv要素の中で data-checkd 属性が true のものだけを拾い出して、それらに紐づく全Accordionインスタンスについて、openメソッドを実行する。 という処理をやってくれるようなtaticなメソッドです。 > 上記ですとうまくいかないのですが、 との、 //アコーディオンを開く処理 $('.js-accordion[data-checkd]').addClass('is-active'); というコードは、先の > メニューを閉じた際にアコーディオンも閉じる処理を無理矢理書いた コードと同じで、先のコメントに書いた > 「原則、アコーディオンの外部からはAccordionクラスやインスタンスを通じてのみ操作を行う」 というルールを破るものになっています。これを破るコードを(うっかりも含め)書いてしまうと、Accordionインスタンスのメソッド open や close から行う、DOMに対するひとまとまりで行うべき処理とは異なる処理を許容することになり、AccordionインスタンスとDOM要素の状態との整合性が崩れてしまいかねないです。 このように、今の作りを維持する限りは、Accordionインスタンスの保持するプロパティや、メソッドから行うDOMに対するひとまとまりの処理と、これに対応する<div class="js-accordion">・・・</div> の部分のDOM要素の状態との整合に常に配慮しないといけないというコストが生じます。 つまり、わかりやすく言うと、 $('.js-accordion[data-checkd]').addClass('is-active'); みたいなコードを書くとアコーディオンが壊れる ぐらいの意識でいる必要があります。(こういう意識でコード書くの、けっこうツライですね。分かります)
noel0718

2021/10/03 13:45

汎用的ではないですよね・・。いろんなことを想定してつくらないと気が済まない性格ですので、 既述自体を全部考え直してみます・・。 Reactは検討しましたが、社内で誰も使ってないので導入が難しい気がします。 今回のAccordionを作った経緯はslidedown・slideupを使わないかつアクセシビリティに対応した記述で作りかったのでcodepenなどを見て作った次第です。
guest

0

アコーディオンを最初から開いた状態は以下のようにしたら、できました。

javascript

1class Accordion { 2 constructor(element) { 3 this.element = element; 4 this.tabs = this.element.querySelector('.js-accordion__tab'); 5 this.panels = this.element.querySelector('.js-accordion__panel'); 6 this.isOpen = false; 7 this.height = 0; 8 this.events(); 9 this.init(); 10 } 11 events() { 12 this.tabs.addEventListener('click', this.handleClick.bind(this)); 13 this.panels.addEventListener('transitionend', this.handleTransition.bind(this)); 14 } 15 init() { 16 if (!this.element.classList.contains('is-active')){ 17 this.close(); 18 }else{ 19 this.open(); 20 this.height = this.panels.scrollHeight; 21 } 22 } 23 handleClick() { 24 if (this.isOpen) { 25 this.close(); 26 } else { 27 this.open(); 28 this.height = this.panels.scrollHeight; 29 console.log(this.height); 30 } 31 } 32 open() { 33 this.isOpen = true; 34 this.element.classList.add('is-active'); 35 this.element.classList.remove('is-hidden'); 36 this.panels.style.maxHeight = `${0}px`; 37 this.tabs.setAttribute('aria-expanded', "true"); 38 this.panels.setAttribute('aria-hidden', "false"); 39 setTimeout(() => { 40 this.panels.style.maxHeight = `${this.height}px`; 41 }, 1); 42 } 43 close() { 44 this.isOpen = false; 45 this.panels.style.maxHeight = `${this.height}px`; 46 this.tabs.setAttribute('aria-expanded', "false"); 47 this.panels.setAttribute('aria-hidden', "true"); 48 setTimeout(() => { 49 this.panels.style.maxHeight = `${0}px`; 50 }, 1); 51 } 52 handleTransition() { 53 if (!this.isOpen) { 54 this.element.classList.add('is-hidden'); 55 this.element.classList.remove('is-active'); 56 } 57 this.panels.style.maxHeight = ''; 58 } 59} 60Array.from(document.querySelectorAll('.js-accordion')).forEach((el) => { 61 new Accordion(el, { 62 tabs: this.tabs, 63 panels: this.panels 64 }); 65});

投稿2021/09/22 12:49

編集2021/09/22 12:54
noel0718

総合スコア20

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

「最初から開いた状態」については、コンストラクタで属性を拾って状態変更をすればいいかと思います。


「1つ開いたら、他が閉じる状態」については、たとえばコンストラクタで静的メンバにインスタンスの配列を保存しておいて、それを利用するというのはどうでしょうか。
ただし、アコーディオンが入れ子になっている時の動作をどうするか、考えないといけないのではないかな、と思いました。

投稿2021/09/21 03:10

Lhankor_Mhy

総合スコア36163

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

noel0718

2021/09/21 11:48

ありがとうございます。 具体的な記述はどのようになりますでしょうか。
Lhankor_Mhy

2021/09/21 12:54

if (element.classList.contains('is-active')){ //表示する処理 } のような感じでしょうか。 --- こっちも if (element.classList.contains('--one')){ } として、thisを配列にpush()すればいいと思います。
noel0718

2021/09/21 13:09

ありがとうございます。ちょっとそちらでやってみます。 またわからない点出てきましたら質問させてください。
noel0718

2021/09/22 12:51

アコーディオンを最初から開いた状態は解決しましたが、アコーディオンを1つ開いたら、他が閉じる状態になるのがどうしてもできませんでした・・。 もう少し教えていただけますと助かります。
Lhankor_Mhy

2021/09/23 12:05

アコーディオンを開く前に、すべてのアコーディオンを閉じればいいと思います。 ですので、すべてのアコーディオンのインスタンスをどこかに保存しておけばよい、ということになります。 回答に書いたのは、クラスのプライベートプロパティに持たせてコンストラクタで追加するのはどうか、という提案です。
noel0718

2021/09/23 16:18

仰ってることはわかるのですが、 コードで書くとどのような形になりますか?
Lhankor_Mhy

2021/09/24 00:37

list がインスタンスの配列だとすると、 list.forEach(x=>{ // 閉じる処理 }) のような形になるのではないでしょうか。
Lhankor_Mhy

2021/09/28 05:48

難しいですか? ご不明の点があればコメントしてください。
noel0718

2021/09/28 15:04

教えていただいたやり方を参考に 格闘してますが、まだうまくいってない状態です。
Lhankor_Mhy

2021/09/29 00:33

問題になっている部分をご提示いただければ、お役に立てることがあるかもしれません。 必要があればコメントしてください。
noel0718

2021/09/29 15:39

別の方からご回答をいただきまして、解消できそうです。 気にかけていただき、ありがとうございます。 また解決しましたら記載いたします。
Lhankor_Mhy

2021/09/30 00:39

ご解決されて何よりです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.46%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問