Erick
Erick

Reputation: 368

Creating a tab controller in javascript?

I'm trying to make a tab controller on a div that has divs beneath it that are for content (each sibling is a tab).

Given this html:

  <div id="myTabCtl">

    <div id="tab1">
      <img src="https://images.unsplash.com/photo-1546238232-20216dec9f72?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1000&q=80" style="width:240px;">
    </div>
    <div id="tab2">
      Cute puppies.
      <img src="https://www.telegraph.co.uk/content/dam/news/2016/05/06/rexfeatures_4950182a_trans_NvBQzQNjv4Bqeo_i_u9APj8RuoebjoAHt0k9u7HhRJvuo-ZLenGRumA.jpg?imwidth=240" style="width:240px;">
    </div>
    <div id="tab3">
      No puppies here.
    </div>

  </div>

And this javascript:

  Element.prototype.tabs = function () {

    var tabs = [];

    this.classList.add('xTabCtl');

    var tab_bar = document.createElement('ul');
    tab_bar.classList.add('xTabRow');
    this.insertBefore(tab_bar, this.firstChild);

    this.addTab = function (div,label,title,click) {
      if (typeof div == 'string')
        div = document.getElementById(div);
      if (!div) return false;

      div.style.display = 'none';

      var tab_ctl = document.createElement('li');
      tab_ctl.innerHTML = label;
      if (title) tab_ctl.title = title;
      var cnt = tabs.length;
      if (click) {
        tab_ctl.onclick = function () {
          this.setTab(cnt);
          click();
        };
      } else {
        tab_ctl.onclick = function () {
          this.setTab(cnt);
        };
      }
      tab_ctl.style.cursor = 'pointer';
      tab_ctl.style.userSelect = 'none';
      tab_ctl.classList.add('xTabItem');
      tab_bar.appendChild(tab_ctl);

      var tab = {};
      tab.elem = tab_ctl;
      tab.div = div;
      tab.loaded = false;
      tabs[tabs.length] = tab;

      return true;
    }

    this.setTab = function (tab_no) {
      for (var i=0; i<tabs.length; i++) {
        tabs[i].elem.classList.remove('xTabItemSel');
        tabs[i].div.style.display = 'none';
        tabs[i].loaded = false;
      }
      tabs[tab_no].div.style.display = 'block';
      tabs[tab_no].div.classList.add('xTabItemSel');
      tabs[tab_no].loaded = true;
      return;
    }

  };

  document.getElementById('myTabCtl').tabs();
  document.getElementById('myTabCtl').addTab('tab1','Cute Puppies 1','Title 1');
  document.getElementById('myTabCtl').addTab('tab2','Cute Puppies 2','Title 2');
  document.getElementById('myTabCtl').addTab('tab3','No Puppies','Title 3',function(){alert('hello.');});

Obviously, this uses some classes that are defined in a theme css file, but that's not at issue.

addTab works, but I cannot call setTab from the onclick of each li that is added. Why not?

Perhaps bigger, there has to be a cleaner way of doing this. I like that it adds the tabs as an li so the dopes I work with only have to add the wrapper and that content. There could be multiple tab controls on a screen. I'm not using the libraries that are available because most go way too far and I want to confine it to addTab and setTab.

Upvotes: 0

Views: 227

Answers (1)

Prashant Sharma
Prashant Sharma

Reputation: 139

This problem is with clouser, when you invoke this.setTab inside event handle, the reference of this is local so it will not get actual setTab method.

Element.prototype.tabs = function () {

    var tabs = [], _this = this;

    this.classList.add('xTabCtl');

    var tab_bar = document.createElement('ul');
    tab_bar.classList.add('xTabRow');
    this.insertBefore(tab_bar, this.firstChild);


    this.addTab = function (div,label,title,click) {
      if (typeof div == 'string')
        div = document.getElementById(div);
      if (!div) return false;

      div.style.display = 'none';

      var tab_ctl = document.createElement('li');
      tab_ctl.innerHTML = label;
      if (title) tab_ctl.title = title;
      var cnt = tabs.length;
      if (click) {
        tab_ctl.onclick = function () {
          _this.setTab(cnt);
          click();
        };
      } else {
        tab_ctl.onclick = function () {
          _this.setTab(cnt);
        };
      }
      tab_ctl.style.cursor = 'pointer';
      tab_ctl.style.userSelect = 'none';
      tab_ctl.classList.add('xTabItem');
      tab_bar.appendChild(tab_ctl);

      var tab = {};
      tab.elem = tab_ctl;
      tab.div = div;
      tab.loaded = false;
      tabs[tabs.length] = tab;

      return true;
    }

    this.setTab = function (tab_no) {
      for (var i=0; i<tabs.length; i++) {
        tabs[i].elem.classList.remove('xTabItemSel');
        tabs[i].div.style.display = 'none';
        tabs[i].loaded = false;
      }
      tabs[tab_no].div.style.display = 'block';
      tabs[tab_no].div.classList.add('xTabItemSel');
      tabs[tab_no].loaded = true;
      return;
    }

  };

  document.getElementById('myTabCtl').tabs();
  document.getElementById('myTabCtl').addTab('tab1','Cute Puppies 1','Title 1');
  document.getElementById('myTabCtl').addTab('tab2','Cute Puppies 2','Title 2');
  document.getElementById('myTabCtl').addTab('tab3','No Puppies','Title 3',function(){alert('hello.');});

Upvotes: 1

Related Questions