1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 // Scroll handling. 6 // 7 // Switches the sidebar between floating on the left and position:fixed 8 // depending on whether it's scrolled into view, and manages the scroll-to-top 9 // button: click logic, and when to show it. 10 (function() { 11 12 var sidebar = document.querySelector('.inline-toc'); 13 var articleBody = document.querySelector('[itemprop="articleBody"]'); 14 15 // Bomb out unless we're on an article page and have a TOC. 16 // Ideally, template shouldn't load this JS file on a non-article page 17 if (!(sidebar && articleBody)) { 18 return; 19 } 20 21 var isLargerThanMobileQuery = 22 window.matchMedia('screen and (min-width: 581px)'); 23 24 var toc = sidebar.querySelector('#toc'); 25 var tocOffsetTop = sidebar.offsetParent.offsetTop + toc.offsetTop; 26 27 function updateTocOffsetTop() { 28 // Note: Attempting to read offsetTop with sticky on causes toc overlap 29 toc.classList.remove('sticky'); 30 tocOffsetTop = sidebar.offsetParent.offsetTop + toc.offsetTop; 31 } 32 33 function addPermalink(el) { 34 el.classList.add('has-permalink'); 35 var id = el.id || el.textContent.toLowerCase().replace(' ', '-'); 36 el.insertAdjacentHTML('beforeend', 37 '<a class="permalink" title="Permalink" href="#' + id + '">#</a>'); 38 } 39 40 // Add permalinks to heading elements. 41 function addPermalinkHeadings(container) { 42 if (container) { 43 ['h2','h3','h4'].forEach(function(h, i) { 44 [].forEach.call(container.querySelectorAll(h), addPermalink); 45 }); 46 } 47 } 48 49 function toggleStickySidenav(){ 50 toc.classList.toggle('sticky', window.scrollY >= tocOffsetTop); 51 } 52 53 function onScroll(e) { 54 toggleStickySidenav(); 55 } 56 57 function onMediaQuery(e) { 58 if (e.matches) { 59 // On tablet & desktop, show permalinks, manage TOC position. 60 document.body.classList.remove('no-permalink'); 61 document.addEventListener('scroll', onScroll); 62 updateTocOffsetTop(); 63 toggleStickySidenav(); 64 } else { 65 // On mobile, hide permalinks. TOC is hidden, doesn't need to scroll. 66 document.body.classList.add('no-permalink'); 67 document.removeEventListener('scroll', onScroll); 68 } 69 } 70 71 // Toggle collapsible sections (mobile). 72 articleBody.addEventListener('click', function(e) { 73 if (e.target.localName == 'h2' && !isLargerThanMobileQuery.matches) { 74 e.target.parentElement.classList.toggle('active'); 75 } 76 }); 77 78 toc.addEventListener('click', function(e) { 79 // React only if clicking on a toplevel menu anchor item 80 // that is not currently open 81 if (e.target.classList.contains('hastoc') && 82 !e.target.parentElement.classList.contains('active')) { 83 e.stopPropagation(); 84 85 // close any previously open subnavs 86 [].forEach.call(toc.querySelectorAll('.active'), function(li) { 87 li.classList.remove('active'); 88 }); 89 90 // then open the clicked one 91 e.target.parentElement.classList.add('active'); 92 } 93 }); 94 95 // Add +/- expander to headings with subheading children. 96 [].forEach.call(toc.querySelectorAll('.toplevel'), function(heading) { 97 if (heading.querySelector('.toc')) { 98 heading.firstChild.classList.add('hastoc'); 99 } 100 }); 101 102 isLargerThanMobileQuery.addListener(onMediaQuery); 103 onMediaQuery(isLargerThanMobileQuery); 104 105 addPermalinkHeadings(articleBody); 106 107 }()); 108