Edit in JSFiddle

const generateToc = (target) => {

  if (target.length) {

    let toc = $('<section class="toc"></section>'),
        title = $('<h2 class="toc_title">toc<br>ー</h2>'),
        ul = $('<ul class="toc_list"></ul>'),
        html = toc.prepend(title),
        h2 = target.find('h2'),
        anchorName = 'section',
        text,
        childText;

    // - -
    // create list item
    // - - - - - - - - -
    const createItem = () => {

      for (let i = 0, len = h2.length; i < len; i++) {

        let child = $('<ul class="toc_child"></ul>'),
            anchorId = anchorName + i;

        $(h2[i]).attr('id', anchorId).nextUntil('h2').filter('h3').each((ci, cv) => {

          let anchorChildId = anchorId + '-' + ci;

          childText = $(cv).attr('id', anchorChildId).html();
          $('<li class="toc_item-child"></li>').text(childText).appendTo(child).wrapInner(() => {
            return '<a class="toc_target" href="#' + anchorChildId + '"></a>';
          });

        });

        text = $(h2[i]).html();
        $('<li class="toc_item"></li>').wrapInner(() => {
            return '<a class="toc_target" href="#' + anchorId + '">' + text + '</a>';
          }).appendTo(ul).append(child);

      }

    };
    createItem();

    // - -
    // insert toc
    // - - - - - - - - -
    const insertToc = () => {

      if (ul.find('li').length > 1) {

        html.append(ul);
        target.before(html);

      }

    };
    insertToc();

    // - -
    // toggle button
    // - - - - - - - - - -
    const clickBtn = () => {

      title.on('click', () => {

        toc.toggleClass('is-active');
        ul.stop().slideToggle(250);

      });

    };
    clickBtn();

    // - -
    // page scroll
    // - - - - - - - - - -
    const scroll = () => {

      toc.find('a').on('click', function(e) {
      
          let anchor = $(this.hash),
							time = 550,
              topDistance = 15,
              anchorOffset = anchor.offset().top - topDistance;

          $('html,body').animate({
            scrollTop: anchorOffset
          }, {
            duration: time
          }, 'easeOutCubic');

      });

    };
    scroll();

  }

};
generateToc($('#js-toc'));
<h1 class="h1">Generate toc from titles</h1>

<div class="wrap">
  <div class="inner" id="js-toc">
    <section class="section">
      <h2 class="h2">section 1</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Enim quasi quos, quia officia, laborum aut ullam vitae aliquam tenetur rem libero, rerum consequuntur iusto odio vero odit porro perferendis error?</p>
      <h3 class="h3">child 1-1</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h3 class="h3">child 1-2</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h2 class="h2">section 2</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Sunt magnam velit, adipisci dolor, eius voluptate, animi suscipit cumque dicta blanditiis molestiae voluptatem molestias veritatis laborum autem odit laboriosam impedit non!</p>
      <h3 class="h3">child 2-1</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h2 class="h2">section 3</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eum dolorem nam eos qui ab placeat optio vitae rem, deserunt incidunt consequatur animi quisquam natus quas, atque eligendi quasi accusantium esse!</p>
      <h3 class="h3">child 3-1</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h3 class="h3">child 3-2</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h2 class="h2">section 4</h2>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Eum dolorem nam eos qui ab placeat optio vitae rem, deserunt incidunt consequatur animi quisquam natus quas, atque eligendi quasi accusantium esse!</p>
      <h3 class="h3">child 4-1</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
      <h3 class="h3">child 4-2</h3>
      <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit. Quasi a veritatis porro soluta deleniti cum id pariatur aut voluptatibus, sint sunt reiciendis itaque excepturi asperiores minus nesciunt voluptates delectus labore!</p>
    </section>
  </div>
</div>
<link href="https://fonts.googleapis.com/css?family=Dancing+Script:400,700" rel="stylesheet">
body {
	width: 100%;
  min-height: 100vh;
  background-image: linear-gradient(60deg, #F7CAC9 15%, #91A8D0 85%);
}

p {
  text-align: justify;
}

.h1 {
  margin: 1.2em;
  font-family: 'Dancing Script', cursive;
  color: rgba(white, .85);
}

.h2 {
  font-size: 42px;
  font-family: 'Dancing Script', cursive;
  color: rgba(white, .85);
}

.h3 {
  font-size: 24px;
  font-family: 'Dancing Script', cursive;
  color: rgba(white, .85);
}

.wrap {
  display: flex;
  justify-content: center;
  align-items: center;
}

.inner {
  padding-right: 1em;
  padding-left: 1em;
	min-width: 300px;
}

.list {
  display: flex;
}

.item {
  font-size: 2em;
  + .item {
    margin-left: 2em;
  }
}

.section {
  max-width: 480px;
  padding: 1em;
  border: 6px double currentColor;
}

// toc
// - - - - - - - - - - 
.toc {
  position: fixed;
  top: 1em;
  left: 1em;
	overflow: auto;
  padding: 1em;
  height: auto;
  max-height: calc(100vh - 2em);
  transition: .25s ease-out;
  border: 2px solid currentColor;
  opacity: .3;
  //
  &.is-active {
    background-color: rgba(#fff, .45);
    border-color: rgba(#000,0);
    box-shadow: 0 0 20px rgba(#000,.1);
    opacity: 1;
  }
  &:hover {
    opacity: 1;
  }
}

.toc_title {
  text-transform: uppercase;
  letter-spacing: .2em;
  cursor: pointer;
}

.toc_list {
  display: none;
}

.toc_item {
  text-transform: capitalize;
  letter-spacing: .1em;
  font-style: italic;
  font-size: 20px;
  //
  + .toc_item {
    margin-top: 1em;
  }
}

.toc_item-child {
  font-size: 14px;
  padding-left: 1em;
}