The script has a good idea, but it is written with errors. Help fix the script errors:

Cannot read property 'querySelectorAll' of null (...)

HTML

<ul class="tabs" data-directive="tabs"> <li><a href="#one" data-toggle="tab">1</a></li> <li><a href="#two" data-toggle="tab">2</a></li> </ul> <div class="container-tabs"> <section id="one"> <p>1</p> </section> <section id="two"> <p>2</p> </section> </div> 

js

 !function (t, e, i) { "function" == typeof define && define.amd ? define(i) : "undefined" != typeof module && module.exports ? module.exports = i() : e[t] = i(); }("Tabs", this, function () { "use strict"; function t(t) { var e, i; this.tabs = t.querySelectorAll("[data-toggle=tab]"); for (this.target = t, this.panels = [], e = 0, i = this.tabs.length; i > e; e++) this.panels.push(document.getElementById(this.tabs[e].hash.replace("#", ""))); void 0 === this.selectedIndex && this._init(); for (var a = this.target.getElementsByTagName("li"), s = 0; s < a.length; s++) a[s].setAttribute('role', 'presentation'); } return t.prototype._init = function () { var t, e = this; for (this.target.setAttribute("role", "tablist"), t = this.tabs.length - 1; t >= 0; t--) { var i = this.tabs[t], a = this.panels[t], s = 0 === t || window.location.hash.replace("#", "") == a.id; i.setAttribute("role", "tab"), i.setAttribute("aria-selected", s), i.setAttribute("aria-controls", i.hash.replace("#", "")), a.setAttribute("role", "tabpanel"), a.setAttribute("tabindex", "0"), a.setAttribute("aria-labelledby", i.hash.replace("#", "")), s ? this.selectedIndex = t : a.setAttribute("aria-hidden", !0); } this.clickHandler = function (t) { var i = t.srcElement || t.target; "tab" == i.getAttribute("role") && (t.preventDefault ? t.preventDefault() : t.returnValue = !1, e.toggle(i)); }, this.keyHandler = function (t) { switch (t.keyCode) { case 37: e.tabs[e.selectedIndex - 1] && e.toggle(e.tabs[e.selectedIndex - 1]); break; case 39: e.tabs[e.selectedIndex + 1] && e.toggle(e.tabs[e.selectedIndex + 1]); } }, this.target.addEventListener ? (this.target.addEventListener("click", this.clickHandler, !1), this.target.addEventListener("keyup", this.keyHandler, !1)) : (this.target.attachEvent("onclick", this.clickHandler), this.target.attachEvent("onclick", this.keyHandler)); }, t.prototype.toggle = function (t) { var e, i, a = document.getElementById(t.hash.replace("#", "")); for (t.focus(), this.tabs[this.selectedIndex].setAttribute("aria-selected", !1), this.panels[this.selectedIndex].setAttribute("aria-hidden", !0), t.setAttribute("aria-selected", !0), a.setAttribute("aria-hidden", !1), e = 0, i = this.tabs.length; i > e && t != this.tabs[e]; e++) ; this.selectedIndex = e; }, t.prototype.teardown = function () { var t, e; this.target.removeAttribute("role"); for (this.target.removeEventListener ? (this.target.removeEventListener("click", this.clickHandler, !1), this.target.removeEventListener("click", this.keyHandler, !1)) : (this.target.detachEvent("onclick", this.clickHandler), this.target.detachEvent("onclick", this.keyHandler)), t = 0, e = this.tabs.length; e > t; t++) { var i = this.tabs[t], a = this.panels[t]; i.removeAttribute("role"), i.removeAttribute("aria-selected"), i.removeAttribute("aria-controls"), a.setAttribute("aria-hidden", !1), a.removeAttribute("role"); } delete this.selectedIndex; }, t; }); var instance = new Tabs(document.querySelector("[data-directive=tabs]")); var instance = new Tabs(document.querySelector("[data-directive=tabs-2]")); var instance = new Tabs(document.querySelector("[data-directive=tabs-3]")); var instance = new Tabs(document.querySelector("[data-directive=tabs-4]")); var r = document.querySelector('.container-tabs'); r.setAttribute('aria-live', 'polite'), r.setAttribute('role', 'region'); 
  • function t (t) {- that's better not to do so - Grundy
  • and where did you get this script? - Grundy
  • the problem is that you have no elements in the markup with [data-directive = tabs-2] , etc. document.querySelector returns null in this case, therefore everything within the function falls - Grundy
  • The abundance of single-letter variables (for storing everything is hidden, and not just counters), suggests that you write immediately obfuscated code. Although you are not good at it, the code can still be read. To confuse it, I even recommend strongly obfuscating the function house, for example, this.target.getElementsByTagName(...) define the variable u="getElementsByTagName" and write this.target[u](...) . - Arnial
  • @Arnial, but it always seemed to me that obfuscation was done by scripts / programs, and not by a person: D and single-letter variables are just a sign of bad code :) Or is it your sarcasm? :) - gil9red

1 answer 1

As it turned out in the comments, the problem is that the same script connects to different pages with different sets of tabs.

To solve the problem, it is enough to select the initialization itself into a separate file:

 !function (t, e, i) { "function" == typeof define && define.amd ? define(i) : "undefined" != typeof module && module.exports ? module.exports = i() : e[t] = i(); }("Tabs", this, function () { "use strict"; function t(t) { var e, i; this.tabs = t.querySelectorAll("[data-toggle=tab]"); for (this.target = t, this.panels = [], e = 0, i = this.tabs.length; i > e; e++) this.panels.push(document.getElementById(this.tabs[e].hash.replace("#", ""))); void 0 === this.selectedIndex && this._init(); for (var a = this.target.getElementsByTagName("li"), s = 0; s < a.length; s++) a[s].setAttribute('role', 'presentation'); } return t.prototype._init = function () { var t, e = this; for (this.target.setAttribute("role", "tablist"), t = this.tabs.length - 1; t >= 0; t--) { var i = this.tabs[t], a = this.panels[t], s = 0 === t || window.location.hash.replace("#", "") == a.id; i.setAttribute("role", "tab"), i.setAttribute("aria-selected", s), i.setAttribute("aria-controls", i.hash.replace("#", "")), a.setAttribute("role", "tabpanel"), a.setAttribute("tabindex", "0"), a.setAttribute("aria-labelledby", i.hash.replace("#", "")), s ? this.selectedIndex = t : a.setAttribute("aria-hidden", !0); } this.clickHandler = function (t) { var i = t.srcElement || t.target; "tab" == i.getAttribute("role") && (t.preventDefault ? t.preventDefault() : t.returnValue = !1, e.toggle(i)); }, this.keyHandler = function (t) { switch (t.keyCode) { case 37: e.tabs[e.selectedIndex - 1] && e.toggle(e.tabs[e.selectedIndex - 1]); break; case 39: e.tabs[e.selectedIndex + 1] && e.toggle(e.tabs[e.selectedIndex + 1]); } }, this.target.addEventListener ? (this.target.addEventListener("click", this.clickHandler, !1), this.target.addEventListener("keyup", this.keyHandler, !1)) : (this.target.attachEvent("onclick", this.clickHandler), this.target.attachEvent("onclick", this.keyHandler)); }, t.prototype.toggle = function (t) { var e, i, a = document.getElementById(t.hash.replace("#", "")); for (t.focus(), this.tabs[this.selectedIndex].setAttribute("aria-selected", !1), this.panels[this.selectedIndex].setAttribute("aria-hidden", !0), t.setAttribute("aria-selected", !0), a.setAttribute("aria-hidden", !1), e = 0, i = this.tabs.length; i > e && t != this.tabs[e]; e++) ; this.selectedIndex = e; }, t.prototype.teardown = function () { var t, e; this.target.removeAttribute("role"); for (this.target.removeEventListener ? (this.target.removeEventListener("click", this.clickHandler, !1), this.target.removeEventListener("click", this.keyHandler, !1)) : (this.target.detachEvent("onclick", this.clickHandler), this.target.detachEvent("onclick", this.keyHandler)), t = 0, e = this.tabs.length; e > t; t++) { var i = this.tabs[t], a = this.panels[t]; i.removeAttribute("role"), i.removeAttribute("aria-selected"), i.removeAttribute("aria-controls"), a.setAttribute("aria-hidden", !1), a.removeAttribute("role"); } delete this.selectedIndex; }, t; }); 

Which is connected "as is" to the pages that need tabs.

And on a specific page, to perform additional code, for example, if there is a tab on a page with selectors "[data-directive = tabs]" and "[data-directive = tabs-2]", then it is enough to use the library only for them

 var instance = new Tabs(document.querySelector("[data-directive=tabs]")); var instance2 = new Tabs(document.querySelector("[data-directive=tabs-2]")); 

By analogy is to do with other pages. So that instances are created only for items that are on the page. Otherwise there will be an error indicated in the question.