├── README.md
├── iconfont
├── iconfont.eot
├── iconfont.ttf
├── iconfont.woff
├── iconfont.woff2
└── iconfont.svg
├── index.html
├── styles
├── style.css
└── tab.css
└── index.js
/README.md:
--------------------------------------------------------------------------------
1 | # tab-oop
2 |
--------------------------------------------------------------------------------
/iconfont/iconfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TianchengLee/tab-oop/master/iconfont/iconfont.eot
--------------------------------------------------------------------------------
/iconfont/iconfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TianchengLee/tab-oop/master/iconfont/iconfont.ttf
--------------------------------------------------------------------------------
/iconfont/iconfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TianchengLee/tab-oop/master/iconfont/iconfont.woff
--------------------------------------------------------------------------------
/iconfont/iconfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/TianchengLee/tab-oop/master/iconfont/iconfont.woff2
--------------------------------------------------------------------------------
/iconfont/iconfont.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
30 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | 面向对象 Tab
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Js 面向对象 动态添加标签页
18 |
19 |
20 |
21 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/styles/style.css:
--------------------------------------------------------------------------------
1 | @font-face {font-family: "iconfont";
2 | src: url('./iconfont/iconfont.eot?t=1553960438096'); /* IE9 */
3 | src: url('./iconfont/iconfont.eot?t=1553960438096#iefix') format('embedded-opentype'), /* IE6-IE8 */
4 | url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAK4AAsAAAAABmwAAAJrAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCCcAp4fwE2AiQDCAsGAAQgBYRtBzAbpQXIrrApw71oi3CCOyzEy8RvE4yIN8TD036/zp03qCYRjaJZNBFFS/gREoRGipQKofjuNrb+9XbTqrmXcqWzfTRDqFqWkhAJzYToaE6LQ7Q30CirRqSKMnj58DdIdrNAdhoTQJa5VGfLrtiAy+lPoAcZdUC57UljTR4TMAo4oL0xiqwYG8YueIHPCdTqYajty/t+bUpmrwvEnUK42lQhLMssVy1UNhzN4kmF6vSQVvMY/T5+HEU1SUXBbti7uBBrx++cgqJULp0GhAgBna5AgSkgE0eN6R1NwTitNt0yAI5VG7wr/8AljmoX7K+zq+tBF1Q8k9JTPWp1AjnJDgCzmM3bU0V31dsvV3M2eC6fHjaGfX/qS7U5Gr58vj6uD0bgxudyrV/OtHHyP+NZnpO1txbktjdY+3FB61+7nxeOzq8niGYnRwT3v3aZxeXf6rrNxl5//49WlEtZUUL1Pj3Bv1EO7MuG2namrCkbvcnApLUJtWpRhv2tzlRLx43kQ7WO2/FW6c5QqDZEZnYKFeosoVK1NdSa5E/XaVM1Ra7BhAEQmk0kjV5QaLbIzG5U6HRRqTkK1DqJtivrjMT1zJaNnIsihAiyQE3JdbszcW0Xiadzdl4d8UO0HSUGNDNXzl2hifYSO5pPjrorgdjUAAavoa5TKDZVUXD3kuuOOzh70fShvUiN2owtNsRxIREIIiATUCYpGO2aqXy/CxEeHcfuaKrLDiGbQ5kcEMsNIK8M5qCmR3mn8RFHOpcECBtlAAwWIZ2OAqV5kQoJXHvShORYBzrDZKhhb3uT8QPlrA3bmsKZV6i89DiTV2o1AAAA') format('woff2'),
5 | url('./iconfont/iconfont.woff?t=1553960438096') format('woff'),
6 | url('./iconfont/iconfont.ttf?t=1553960438096') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
7 | url('./iconfont/iconfont.svg?t=1553960438096#iconfont') format('svg'); /* iOS 4.1- */
8 | }
9 |
10 | .iconfont {
11 | font-family: "iconfont" !important;
12 | font-size: 16px;
13 | font-style: normal;
14 | -webkit-font-smoothing: antialiased;
15 | -moz-osx-font-smoothing: grayscale;
16 | }
17 |
18 | .icon-guanbi:before {
19 | content: "\e676";
20 | }
21 |
22 |
--------------------------------------------------------------------------------
/styles/tab.css:
--------------------------------------------------------------------------------
1 | * {
2 | margin: 0;
3 | padding: 0;
4 | }
5 |
6 | ul li {
7 | list-style: none;
8 | }
9 |
10 | main {
11 | width: 960px;
12 | height: 500px;
13 | border-radius: 10px;
14 | margin: 50px auto;
15 | }
16 |
17 | main h4 {
18 | height: 100px;
19 | line-height: 100px;
20 | text-align: center;
21 | }
22 |
23 | .tabsbox {
24 | width: 900px;
25 | margin: 0 auto;
26 | height: 400px;
27 | border: 1px solid lightsalmon;
28 | position: relative;
29 | }
30 |
31 | nav ul {
32 | overflow: hidden;
33 | }
34 |
35 | nav ul li {
36 | float: left;
37 | width: 100px;
38 | height: 50px;
39 | line-height: 50px;
40 | text-align: center;
41 | border-right: 1px solid #ccc;
42 | position: relative;
43 | }
44 |
45 | nav ul li.liactive {
46 | border-bottom: 2px solid #fff;
47 | z-index: 9;
48 | }
49 |
50 | #tab input {
51 | width: 80%;
52 | height: 60%;
53 | }
54 |
55 | nav ul li span:last-child {
56 | position: absolute;
57 | user-select: none;
58 | font-size: 12px;
59 | top: -18px;
60 | right: 0;
61 | display: inline-block;
62 | height: 20px;
63 | }
64 |
65 | .tabadd {
66 | position: absolute;
67 | /* width: 100px; */
68 | top: 0;
69 | right: 0;
70 | }
71 |
72 | .tabadd span {
73 | display: block;
74 | width: 20px;
75 | height: 20px;
76 | line-height: 20px;
77 | text-align: center;
78 | border: 1px solid #ccc;
79 | float: right;
80 | margin: 10px;
81 | user-select: none;
82 | }
83 |
84 | .tabscon {
85 | width: 100%;
86 | height: 300px;
87 | position: absolute;
88 | padding: 30px;
89 | top: 50px;
90 | left: 0px;
91 | box-sizing: border-box;
92 | border-top: 1px solid #ccc;
93 | }
94 |
95 | .tabscon section,
96 | .tabscon section.conactive {
97 | display: none;
98 | width: 100%;
99 | height: 100%;
100 | }
101 |
102 | .tabscon section.conactive {
103 | display: block;
104 | }
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | class Tab {
2 |
3 | // 抽取对象 共有的方法 对象就是 tab
4 | // tab 功能 有 点击切换 点击 + 添加 tab 点击 减去 删除tab
5 | constructor(id) {
6 | // 最大的盒子
7 | this.id = document.querySelector(id);
8 | this.firstNav = this.id.querySelector('.fisrstnav');
9 | this.tabsCon = this.id.querySelector('.tabscon');
10 | // 里面的加号
11 | this.add = this.id.querySelector('.tabadd');
12 | // 获取静态列表
13 | this.updateNodeList();
14 | // 注册事件
15 | this.registerAction();
16 | }
17 |
18 | // 重新获取最新的列表
19 | updateNodeList() {
20 | // 上面 li
21 | this.lis = this.id.querySelectorAll('li');
22 | // 下面的切换seciton
23 | this.sections = this.id.querySelectorAll('section');
24 | // 减号
25 | this.jian = this.id.querySelectorAll('.iconfont');
26 | // 双击修改 span
27 | this.titleSpan = this.id.querySelectorAll('.fisrstnav li span:first-child');
28 | }
29 |
30 | registerAction() {
31 | // 添加Tab不需要事件代理
32 | this.add.onclick = this.addTab.bind(this.add, this)
33 | // 事件代理
34 | this.delegate(this.firstNav, 'li', 'click', this.tabToggle)
35 | this.delegate(this.firstNav, '.icon-guanbi', 'click', this.removeTab)
36 | this.delegate(this.firstNav, '.title', 'dblclick', this.change)
37 | this.delegate(this.tabsCon, '.content', 'dblclick', this.change)
38 | }
39 |
40 | // 添加tab
41 | addTab(tab) {
42 | tab.clearClass();
43 | var random = Math.random()
44 | var li = 'Tab'
45 | var section = 'New Tab content' + random + ''
46 |
47 | // 将元素追加到页面结构中去
48 | tab.id.querySelector('.fisrstnav ul:first-child').insertAdjacentHTML('beforeend', li)
49 | tab.id.querySelector('.tabscon').insertAdjacentHTML('beforeend', section)
50 | tab.updateNodeList();
51 | }
52 |
53 | // 切换tab栏
54 | tabToggle(tab) {
55 | tab.clearClass();
56 | this.classList.add('liactive');
57 | var i = Array.prototype.indexOf.call(tab.lis, this)
58 | tab.sections[i].classList.add('conactive');
59 | }
60 |
61 | // 清除原先样式
62 | clearClass() {
63 | for (var i = 0; i < this.lis.length; i++) {
64 | this.lis[i].classList.remove('liactive');
65 | this.sections[i].classList.remove('conactive')
66 | }
67 | }
68 |
69 | // 移除 tab 做法
70 | removeTab(tab) {
71 | var i = Array.prototype.indexOf.call(tab.lis, this.parentNode)
72 |
73 | // console.log(i, tab.lis)
74 | tab.lis[i].remove()
75 | tab.sections[i].remove()
76 | tab.updateNodeList()
77 |
78 | if (--i < 0) i = 0
79 |
80 | if (tab.lis[i]) {
81 | tab.lis[i].click()
82 | }
83 | }
84 |
85 | // 更改文字内容
86 | change() {
87 | window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
88 | var str = this.innerText;
89 | this.innerHTML = '';
90 | var input = this.children[0];
91 | input.value = str;
92 | input.focus();
93 | input.onblur = function () {
94 | this.parentNode.innerHTML = this.value;
95 | }
96 | input.onkeydown = function (e) {
97 | if (e.keyCode === 13) this.blur()
98 | }
99 | }
100 |
101 | // 核心: 事件代理 + 闭包 有一定难度
102 | delegate(parent, selector, eventName, eventHandler) {
103 | function getTarget(ele) {
104 | if (ele.matches(selector)) return ele
105 | while (ele = ele.parentNode) {
106 | if (ele.matches(selector)) {
107 | return ele
108 | }
109 | }
110 | return null
111 | }
112 | parent.addEventListener(eventName, function (e) {
113 | if (e.target.matches(selector) || e.target.matches(selector + ' *')) {
114 | eventHandler.call(getTarget(e.target), this, e);
115 | this.updateNodeList()
116 | }
117 | }.bind(this))
118 | }
119 |
120 | }
121 | var tab = new Tab('#tab');
--------------------------------------------------------------------------------