Search code examples
javascriptjqueryclassthisprototype

How to unfold only a child element of its parents own in Class Syntax?


I'm trying to select a child element of its parent own (a.k.a firstPanel) in Class Syntax but have no idea how would I do this.

It's easy to refer the each firstPanel in the normal function, and it works perfectly fine like this:

$(function accordian() {
  $('.mobileCategory').on('click', function() {
    if (!$(this).hasClass('open')) {
      $(this).addClass('open');
      $(this).siblings('.firstPanel').stop(true, true).animate({
        maxHeight: 1000 + 'px'
      });
    } else if ($(this).hasClass('open')) {
      $(this).removeClass('open');
      $(this).siblings('.firstPanel').stop(true, true).animate({
        maxHeight: null
      });
    }
  })
})

enter image description here

but when I click the element under the Class Syntax, the whole .firstPanel is going to unfold like this:

enter image description here

I know this.list is assuming the entire firstPanel. I just don't know how would I refer the child element of the parents own in the Class Syntax.

Are there any ways to do this?

Full Code:

class Accordian {
  constructor($el) {
    this.$el = $el;
    this.category = this.$el.find('.mobileCategory');
    this.list = this.category.siblings();
    /* Boolean Flags */
    this.flags = {
      active: false,
    };
  }
  clicked(e) {
    console.log('text')
    this.list.css({
      maxHeight: 1000 + 'px'
    })
  }
  manange() {
    console.log(this.list, 'text');
    this.$el.on({['click']: (e) => this.clicked(e)});
  }
}
var thatAccordian = new Accordian($('#mobile-menu'));
    thatAccordian.manange();
a {
  text-decoration: none;
  color: white;
}
#mobile-menu {
  position: absolute;
  z-index: 999;
  color: white;
  width: 100%;
  height: calc(100% - 110px);
  top: 110px;
  background-color: #202020;
}
.wrapper, li {
  display: block;
  flex-flow: column;
  margin: 1rem;
  transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
}
.wrapper > li > ul {
  transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
  display: block;
  flex-flow: column;
  max-height: 0;
  overflow: hidden;
  margin: 0 1rem;
}
.wrapper > li > ul > li {
  transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
  display: block;
  flex-flow: column;
  margin: 1rem 0;
  font-size: .8rem;
  outline-width: 0;
  border-width: 0;
  border-bottom: 1px solid white;
}
  <div id="mobile-menu">
    <ul class="wrapper">
      <li>
        <a href="#" class="mobileCategory">MODELS</a>
        <ul class="firstPanel">
          <li>URUS</li>
          <li>HURACÁN</li>
          <li>AVENTADOR</li>
          <li>FEW OFF</li>
          <li>CONCEPT</li>
          <li>AD PERSONAM</li>
          <li>OVERVIEW</li>
        </ul>
      </li>
      <li>
        <a href="#" class="mobileCategory">BRAND</a>
        <ul class="firstPanel">
          <li>PEOPLE</li>
          <li>HISTORY</li>
          <li>MASTERPIECE</li>
          <li>DESIGN</li>
          <li>INNOVATION & EXCELLENCE</li>
          <li>OVERVIEW</li>
        </ul>
      </li>
      <li>
      <li><a href="#" class="mobileCategory">MOTORSPORT</a></li>
      <li><a href="#" class="mobileCategory">STORE</a></li>
    </ul>
  </div>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>


Solution

  • Well, according to your comments, I think you might want to revisit your Accordian design. Here are a few pointers:

    • You say each mobileCategories is an Accordian but, when you create an Accordian object like this: var thatAccordian = new Accordian($('#mobile-menu'));, you are stating a one-to-one relationship between thatAccordian and #mobile-menu, wouldn't you say?
    • Now take a look at the constructor, when you say this.$el = $el;, this.$el ends up with #mobile-menu, not with one .mobileCatetgory; likewise when you write this.category = this.$el.find('.mobileCategory');, someone might think, by the name category (singular), that category ends up with one .mobileCategory element, but it doesn't. Try console.log-ing it and you'll see how category contains ALL (4 in this snippet) category elements. Finally, the same occurs with this.list = this.category.siblings();; this.list will contain ALL (2 in this snippet) uls inside #mobile-menu which are siblings of a .mobileCategory (which is why two elements are being expanded right now instead of just the one clicked).

    So, my advice, check your Accordian again but, for the time being, here's a snippet which does what you actually wanted.

    As it is right now, inside your clicked(e) function, you need to use the event arg (e) in order to get the exact element being clicked and then apply whatever style is needed for the animation to it, or its siblings. Also, I recommend adding/removing/toggling CSS classes when possible and creating those classes in the CSS file, instead of editing CSS code from JS.

    One last thing, I removed this line height: calc(100% - 110px); in the snippet, otherwise the black background was lost when the list was expanded. Also, run the snippet in full screen mode.

    HIH

    class Accordian {
      constructor($el) {
        this.$el = $el;
        this.category = this.$el.find('.mobileCategory');
        this.list = this.category.siblings();
        
        /* Boolean Flags */
        this.flags = {
          active: false,
        };
      }
      clicked(e) {
        //console.log($(e.target))
        
        $(e.target).siblings('ul').toggleClass('expanded');
        /*this.list.css({
          maxHeight: 1000 + 'px'
        })*/
      }
      manange() {
        //console.log(this.list, 'text');
        this.$el.on({['click']: (e) => this.clicked(e)});
      }
    }
    var thatAccordian = new Accordian($('#mobile-menu'));
        thatAccordian.manange();
    a {
      text-decoration: none;
      color: white;
    }
    #mobile-menu {
      position: absolute;
      z-index: 999;
      color: white;
      width: 100%;
      /*height: calc(100% - 110px);*/
      top: 110px;
      background-color: #202020;
    }
    .wrapper, li {
      display: block;
      flex-flow: column;
      margin: 1rem;
      transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
    }
    .wrapper > li > ul {
      transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
      display: block;
      flex-flow: column;
      max-height: 0;
      overflow: hidden;
      margin: 0 1rem;
    }
    .wrapper > li > ul > li {
      transition: all 500ms cubic-bezier(0.455, 0.03, 0.515, 0.955);
      display: block;
      flex-flow: column;
      margin: 1rem 0;
      font-size: .8rem;
      outline-width: 0;
      border-width: 0;
      border-bottom: 1px solid white;
    }
    
    .wrapper > li > ul.expanded{
      max-height: 1000px;
    }
    <div id="mobile-menu">
        <ul class="wrapper">
          <li>
            <a href="#" class="mobileCategory">MODELS</a>
            <ul class="firstPanel">
              <li>URUS</li>
              <li>HURACÁN</li>
              <li>AVENTADOR</li>
              <li>FEW OFF</li>
              <li>CONCEPT</li>
              <li>AD PERSONAM</li>
              <li>OVERVIEW</li>
            </ul>
          </li>
          <li>
            <a href="#" class="mobileCategory">BRAND</a>
            <ul class="firstPanel">
              <li>PEOPLE</li>
              <li>HISTORY</li>
              <li>MASTERPIECE</li>
              <li>DESIGN</li>
              <li>INNOVATION & EXCELLENCE</li>
              <li>OVERVIEW</li>
            </ul>
          </li>
          <li>
          <li><a href="#" class="mobileCategory">MOTORSPORT</a></li>
          <li><a href="#" class="mobileCategory">STORE</a></li>
        </ul>
      </div>
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>