5
<title>Example: Adding and Removing Tabs</title>
6
<link rel="stylesheet" href="http://yui.yahooapis.com/3.4.0pr3/build/cssgrids/grids-min.css">
7
<link rel="stylesheet" href="../assets/css/main.css">
8
<link rel="stylesheet" href="../assets/vendor/prettify/prettify-min.css">
9
<script src="../../build/yui/yui-min.js"></script>
14
<h1>Example: Adding and Removing Tabs</h1>
19
<div id="main" class="yui3-u">
20
<div class="content"><style scoped>
25
.yui3-skin-sam .yui3-tab {
29
.yui3-skin-sam .yui3-tabview-removeable .yui3-tab-label,
30
.yui3-skin-sam .yui3-tabview-removeable .yui3-tab-selected .yui3-tab-label {
31
padding-right: 1.3em; /* make room for close link */
34
.yui3-skin-sam .yui3-tabview-removeable .yui3-tab-add {
35
padding-right: 0.75em; /* no close link for add tab link */
38
.yui3-skin-sam .yui3-tab-add {
40
font: bold 120%/1 verdana;
46
font:bold 80% verdana;
52
.yui3-tab-remove:hover {
56
.yui3-skin-sam .yui3-tab-selected .yui3-tab-remove {
60
.yui3-skin-sam .yui3-tab-selected .yui3-tab-remove:hover {
64
#main #example-canvas .yui3-tabview .yui3-tab-selected a {
70
<p>This example shows how to give <code>TabView</code> buttons for adding and removing tabs.</p>
73
<div class="example yui3-skin-sam">
76
<li><a href="#foo">foo</a></li>
77
<li><a href="#bar">bar</a></li>
78
<li><a href="#baz">baz</a></li>
99
<script type="text/javascript">
100
YUI().use('tabview', 'escape', 'plugin', function(Y) {
101
var Addable = function(config) {
102
Addable.superclass.constructor.apply(this, arguments);
105
Addable.NAME = 'addableTabs';
106
Addable.NS = 'addable';
108
Y.extend(Addable, Y.Plugin.Base, {
109
ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
110
'<a class="yui3-tab-label yui3-tab-add">+</a></li>',
112
initializer: function(config) {
113
var tabview = this.get('host');
114
tabview.after('render', this.afterRender, this);
115
tabview.get('contentBox')
116
.delegate('click', this.onAddClick, '.yui3-tab-add', this);
119
getTabInput: function() {
120
var tabview = this.get('host');
122
label: Y.Escape.html(window.prompt('label:', 'new tab')),
123
content: '<p>' + Y.Escape.html(window.prompt('content:', 'new content')) + '</p>',
124
index: Number(window.prompt('index:', tabview.size()))
128
afterRender: function(e) {
129
var tabview = this.get('host');
130
tabview.get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
133
onAddClick: function(e) {
135
var tabview = this.get('host'),
136
input = this.getTabInput();
137
tabview.add(input, input.index);
141
var Removeable = function(config) {
142
Removeable.superclass.constructor.apply(this, arguments);
145
Removeable.NAME = 'removeableTabs';
146
Removeable.NS = 'removeable';
148
Y.extend(Removeable, Y.Plugin.Base, {
149
REMOVE_TEMPLATE: '<a class="yui3-tab-remove" title="remove tab">x</a>',
151
initializer: function(config) {
152
var tabview = this.get('host'),
153
cb = tabview.get('contentBox');
155
cb.addClass('yui3-tabview-removeable');
156
cb.delegate('click', this.onRemoveClick, '.yui3-tab-remove', this);
158
// Tab events bubble to TabView
159
tabview.after('tab:render', this.afterTabRender, this);
162
afterTabRender: function(e) {
163
// boundingBox is the Tab's LI
164
e.target.get('boundingBox').append(this.REMOVE_TEMPLATE);
167
onRemoveClick: function(e) {
169
var tab = Y.Widget.getByNode(e.target);
174
var tabview = new Y.TabView({
179
var tabview2 = new Y.TabView({
182
content: '<p>foo content</p>'
185
content: '<p>bar content</p>'
188
content: '<p>baz content</p>'
190
plugins: [Removeable]
193
var tabview3 = new Y.TabView({
196
content: '<p>foo content</p>'
199
content: '<p>bar content</p>'
202
content: '<p>bar content</p>'
204
plugins: [Addable, Removeable]
208
tabview2.render('#demo2');
209
tabview3.render('#demo3');
215
<h2>Plugin Template</h2>
217
<p>In order to make these addons reusable, we can build them as plugins. This
218
allows the option for multiple tabviews that mix and match functionality.
219
To get started, we will first fill in a basic <code>Plugin</code> template.
220
The <code>NAME</code> property is required to prefix events, classNames, et cetera.
221
The <code>NS</code> is the namespace where the plugin will live on the
222
<code>host</code>. This is where its API can be accessed (e.g. "node.addable.destroy()").
223
Adding the <code>this._host</code> alias provides a convenient way to get back to the TabView
224
instance. Calling the superclass constructor kicks off the <code>Base</code> lifecycle,
225
which will call the <code>initializer</code>.
229
<pre class="code prettyprint">var Addable = function(config) {
230
this._host = config.host;
231
Addable.superclass.constructor.apply(this, arguments);
234
Addable.NAME = 'addableTabs';
235
Addable.NS = 'addable';
237
Y.extend(Addable, Y.Plugin.Base, {
238
initializer: function(config) {
243
<h2>Addable Tab Plugin</h2>
245
<p>To simplify adding new tabs, we are going to add a button that
246
users can click and that will prompt them for some details regarding the new tab.
247
The main task we are trying to accomplish is to add some HTML to the
248
<code>TabView</code>, listen for clicks on the button, prompt the user for input,
249
and update the tabs accordingly.</p>
251
<h3>HTML Template</h3>
252
<p>The first thing we need is a template for the markup to be generated. Adding
253
this to the prototype allows separate customization for each <code>TabView</code>
254
instance. For this example, we want it to look and feel like another <code>Tab</code>
255
without actually being one.</p>
257
<pre class="code prettyprint">Y.extend(Addable, Y.Plugin.Base, {
258
ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
259
'<a class="yui3-tab-label yui3-tab-add">+</a></li>',
261
initializer: function(config) {
266
<h3>Adding the HTML</h3>
267
<p>Now that we have a markup template, we will need to add it to the <code>TabView</code>
268
somehow. The <code>render</code> phase is the appropriate moment to do so. Listening
269
for the <code>render</code> event will give us access to that moment. Listening
270
for <code>after('render')</code> ensure that the rendering has actually happened. Then
271
we just need to find the tab list and, using the template, add the new item.
272
The <code>contentBox</code> provides a <code>Node</code> that can be used to manage
273
the <code>TabView</code> HTML.</p>
275
<pre class="code prettyprint">Y.extend(Addable, Y.Plugin.Base, {
276
ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
277
'<a class="yui3-tab-label yui3-tab-add">+</a></li>',
279
initializer: function(config) {
280
var tabview = this.get('host');
281
tabview.after('render', this.afterRender, this);
284
afterRender: function(e) {
285
var tabview = this.get('host');
286
tabview.get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
291
<h3>Handling the Click</h3>
292
<p>All that remains is to listen for clicks on the add button and prompt
293
the user for the relevant <code>Tab</code> data. Again we can leverage
294
the <code>Node</code> API, this time to delegate clicks on the add button.
295
Stopping event propagation in our handler ensures that the event does
296
not bubble any further, preventing the <code>TabView</code> from trying
297
to handle it. To keep the example simple, a basic <code>prompt</code> is
298
used to get the user input. This could be refined to use an
299
<code>Overlay</code> or other custom control. The data is then handed off
300
to <code>TabView</code>'s <code>add</code> method.</p>
302
<pre class="code prettyprint">Y.extend(Addable, Y.Plugin.Base, {
303
ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
304
'<a class="yui3-tab-label yui3-tab-add">+</a></li>',
306
initializer: function(config) {
307
var tabview = this.get('host');
308
tabview.after('render', this.afterRender, this);
310
tabview.get('contentBox')
311
.delegate('click', this.onAddClick, '.yui3-tab-add', this);
314
afterRender: function(e) {
315
this.get('host').get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
318
getTabInput: function() {
320
label: window.prompt('label:', 'new tab'),
321
content: window.prompt('content:', '<p>new content</p>'),
322
index: Number(window.prompt('index:', this._host.size()))
326
onAddClick: function(e) {
328
var tabview = this.get('host');
329
input = this.getTabInput();
330
tabview.add(input, input.index);
335
<h3>Using the Plugin</h3>
336
<p>Now we can go ahead and plug in our functionality. This can be during
337
construction with the <code>plugins</code> attribute, or subsequently
338
via the <code>plug</code> method.</p>
340
<pre class="code prettyprint">var tabview = new Y.TabView({
342
label: 'foo',
343
content: '<p>foo content</p>'
345
label: 'bar',
346
content: '<p>bar content</p>'
348
label: 'baz',
349
content: '<p>baz content</p>'
355
// tabview.plug(Addable);</pre>
358
<h2>Removeable Tabs Plugin</h2>
359
<p>The process for creating a removeable plugin for <code>TabView</code>
360
is very similar. The full source is provided below.</p>
362
<pre class="code prettyprint">var Removeable = function(config) {
363
Removeable.superclass.constructor.apply(this, arguments);
366
Removeable.NAME = 'removeableTabs';
367
Removeable.NS = 'removeable';
369
Y.extend(Removeable, Y.Plugin.Base, {
370
REMOVE_TEMPLATE: '<a class="yui3-tab-remove" title="remove tab">x</a>',
372
initializer: function(config) {
373
var tabview = this.get('host'),
374
cb = tabview.get('contentBox');
376
cb.addClass('yui3-tabview-removeable');
377
cb.delegate('click', this.onRemoveClick, '.yui3-tab-remove', this);
379
// Tab events bubble to TabView
380
tabview.after('tab:render', this.afterTabRender, this);
383
afterTabRender: function(e) {
384
// boundingBox is the Tab's LI
385
e.target.get('boundingBox').append(this.REMOVE_TEMPLATE);
388
onRemoveClick: function(e) {
390
var tab = Y.Widget.getByNode(e.target);
395
<h2>Complete Example Source</h2>
396
<pre class="code prettyprint"><div id="demo">
398
<li><a href="#foo">foo</a></li>
399
<li><a href="#bar">bar</a></li>
400
<li><a href="#baz">baz</a></li>
403
<div id="foo">
404
<p>foo content</p>
406
<div id="bar">
407
<p>bar content</p>
409
<div id="baz">
410
<p>baz content</p>
415
<div id="demo2">
418
<div id="demo3">
421
<script type="text/javascript">
422
YUI().use('tabview', 'escape', 'plugin', function(Y) {
423
var Addable = function(config) {
424
Addable.superclass.constructor.apply(this, arguments);
427
Addable.NAME = 'addableTabs';
428
Addable.NS = 'addable';
430
Y.extend(Addable, Y.Plugin.Base, {
431
ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
432
'<a class="yui3-tab-label yui3-tab-add">+</a></li>',
434
initializer: function(config) {
435
var tabview = this.get('host');
436
tabview.after('render', this.afterRender, this);
437
tabview.get('contentBox')
438
.delegate('click', this.onAddClick, '.yui3-tab-add', this);
441
getTabInput: function() {
442
var tabview = this.get('host');
444
label: Y.Escape.html(window.prompt('label:', 'new tab')),
445
content: '<p>' + Y.Escape.html(window.prompt('content:', 'new content')) + '</p>',
446
index: Number(window.prompt('index:', tabview.size()))
450
afterRender: function(e) {
451
var tabview = this.get('host');
452
tabview.get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
455
onAddClick: function(e) {
457
var tabview = this.get('host'),
458
input = this.getTabInput();
459
tabview.add(input, input.index);
463
var Removeable = function(config) {
464
Removeable.superclass.constructor.apply(this, arguments);
467
Removeable.NAME = 'removeableTabs';
468
Removeable.NS = 'removeable';
470
Y.extend(Removeable, Y.Plugin.Base, {
471
REMOVE_TEMPLATE: '<a class="yui3-tab-remove" title="remove tab">x</a>',
473
initializer: function(config) {
474
var tabview = this.get('host'),
475
cb = tabview.get('contentBox');
477
cb.addClass('yui3-tabview-removeable');
478
cb.delegate('click', this.onRemoveClick, '.yui3-tab-remove', this);
480
// Tab events bubble to TabView
481
tabview.after('tab:render', this.afterTabRender, this);
484
afterTabRender: function(e) {
485
// boundingBox is the Tab's LI
486
e.target.get('boundingBox').append(this.REMOVE_TEMPLATE);
489
onRemoveClick: function(e) {
491
var tab = Y.Widget.getByNode(e.target);
496
var tabview = new Y.TabView({
497
srcNode: '#demo',
501
var tabview2 = new Y.TabView({
503
label: 'foo',
504
content: '<p>foo content</p>'
506
label: 'bar',
507
content: '<p>bar content</p>'
509
label: 'baz',
510
content: '<p>baz content</p>'
512
plugins: [Removeable]
515
var tabview3 = new Y.TabView({
517
label: 'foo',
518
content: '<p>foo content</p>'
520
label: 'bar',
521
content: '<p>bar content</p>'
523
label: 'baz',
524
content: '<p>bar content</p>'
526
plugins: [Addable, Removeable]
530
tabview2.render('#demo2');
531
tabview3.render('#demo3');
533
</script></pre>
538
<div id="sidebar" class="yui3-u">
542
<div class="sidebox">
544
<h2 class="no-toc">Examples</h2>
548
<ul class="examples">
551
<li data-description="This example shows how to create a TabView wigdet from existing HTML.">
552
<a href="tabview-basic.html">TabView from Existing Markup</a>
557
<li data-description="This example shows how to create a TabView wigdet from JavaScript.">
558
<a href="tabview-fromjs.html">Dynamic TabView from JavaScript</a>
563
<li data-description="This example shows how to add and remove Tabs.">
564
<a href="tabview-add-remove.html">Adding and Removing Tabs</a>
569
<li data-description="This example shows how to load tab content remotely using a YQL plugin.">
570
<a href="tabview-yql.html">Loading Tab Content</a>
582
<div class="sidebox">
584
<h2 class="no-toc">Examples That Use This Component</h2>
588
<ul class="examples">
599
<li data-description="Demonstrates how to add browser history support to a TabView widget using the History Utility.">
600
<a href="../history/history-tabview.html">History + TabView</a>
612
<script src="../assets/vendor/prettify/prettify-min.js"></script>
613
<script>prettyPrint();</script>