Your search did not match any results.
Tab Panel

Tab Drag & Drop

This demo illustrates how you can allow end-users to reorder, add, and remove tabs within the DevExtreme TabPanel widget.

Reorder tabs

Wrap the TabPanel into the Sortable widget and set the following options as needed:

  • filter
    Specify a CSS selector to indicate draggable items. This demo sets filter to .dx-tab.

  • itemOrientation
    Set this option to "horizontal". When a user drags a tab, remaining items move left and right to designate the drop target.

  • onReorder
    Implement this handler to synchronize the dataSource with reordered tabs.

  • moveItemOnDrop Set this option to true to update markup after a tab is dropped.

Add or remove tabs

This demo adds and removes tabs in the following two functions:

  • addButtonHandler - a click handler for the "Add Tab" button.
  • closeButtonHandler - a click handler for a tab's Close icon. The demo uses the itemTitleTemplate option to display this icon.

Both handlers modify the TabPanel's dataSource.

Copy to CodePen
Apply
Reset
$(function () { var tabPanel = $("#tabPanel").dxTabPanel({ dataSource: employees.slice(0, 3), itemTitleTemplate: titleTemplate, itemTemplate: itemTemplate, height: 410, deferRendering: false, showNavButtons: true, repaintChangesOnly: true }).dxTabPanel("instance"); $("#tabPanel").dxSortable({ moveItemOnDrop: true, filter: ".dx-tab", itemOrientation: "horizontal", dragDirection: "horizontal", onReorder: function(e) { var tabPanelItems = tabPanel.option("dataSource"); var itemData = tabPanelItems.splice(e.fromIndex, 1)[0]; tabPanelItems.splice(e.toIndex, 0, itemData); tabPanel.option("dataSource", tabPanelItems); tabPanel.option("selectedIndex", e.toIndex); } }); var addTabButton = $("#addButton").dxButton({ icon: "add", text: "Add Tab", type: "default", onClick: addButtonHandler }).dxButton("instance"); function titleTemplate(itemData, itemIndex, itemElement) { itemElement .append($("<span>").text(itemData.FirstName + " " + itemData.LastName)); if(!itemData.isLast) { itemElement .append( $("<i>") .addClass("dx-icon") .addClass("dx-icon-close") .click(function(e) { closeButtonHandler(itemData); }) ); } } function itemTemplate(itemData, itemIndex, itemElement) { $("<div>") .addClass("employeeInfo") .append($('<img class="employeePhoto" src="' + itemData.Picture + '"/>')) .append($("<p>").addClass("employeeNotes").append($("<b>Position: " + itemData.Position + "</b><br/>")).append(itemData.Notes)) .appendTo(itemElement); $("<div>") .addClass("caption") .text(itemData.FirstName + " " + itemData.LastName + "'s Tasks:") .appendTo(itemElement); var employeeTasks = tasks.filter(function(task) { return task.EmployeeID === itemData.ID }); $("<div>") .addClass("task-list") .appendTo(itemElement) .dxList({ dataSource: employeeTasks, selectionMode: "multiple", showSelectionControls: true, disabled: true, selectedItems: employeeTasks.filter(function(task) { return task.Status === "Completed" }), itemTemplate: function(itemData) { return $("<div>" + itemData.Subject + "</div>"); } }); } function addButtonHandler() { var tabPanelItems = tabPanel.option("dataSource"); var newItem = employees.filter(function(employee) { return tabPanelItems.indexOf(employee) === -1; })[0]; tabPanelItems.push(newItem); updateButtonsState(tabPanelItems); tabPanel.option("dataSource", tabPanelItems); tabPanel.option("selectedIndex", tabPanelItems.length - 1); } function closeButtonHandler(itemData) { var index = tabPanel.option("dataSource").indexOf(itemData); var tabPanelItems = tabPanel.option("dataSource"); tabPanelItems.splice(index, 1); updateButtonsState(tabPanelItems); tabPanel.option("dataSource", tabPanelItems); if(index >= tabPanelItems.length && index > 0) tabPanel.option("selectedIndex", index - 1); } function updateButtonsState(items) { addTabButton.option("disabled", items.length === employees.length); items[0].isLast = items.length === 1; } });
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <title>DevExtreme Demo</title> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" /> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script> <script>window.jQuery || document.write(decodeURIComponent('%3Cscript src="js/jquery.min.js"%3E%3C/script%3E'))</script> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/20.2.3/css/dx.common.css" /> <link rel="stylesheet" type="text/css" href="https://cdn3.devexpress.com/jslib/20.2.3/css/dx.light.css" /> <script src="https://cdn3.devexpress.com/jslib/20.2.3/js/dx.all.js"></script> <script src="js/underscore.js"></script> <script src="data.js"></script> <link rel="stylesheet" type="text/css" href="styles.css" /> <script src="index.js"></script> </head> <body class="dx-viewport"> <div class="demo-container"> <div id="container"> <div id="addButton"></div> </div> <div id="tabPanel"></div> </div> </body> </html>
#container { display: flex; justify-content: flex-end; padding-bottom: 10px; } .dx-tabs-wrapper .dx-tab { width: auto; padding-left: 20px; } .dx-tab-content .dx-icon-close { display: inline-block; opacity: 0.6; margin-right: 0; margin-left: 7px; font-size: 18px; } .dx-sortable-dragging .dx-tab { box-sizing: border-box; text-align: center; } .employeeInfo .employeePhoto { height: 100px; float: left; padding: 10px 20px 10px 20px; } .caption { padding: 0px 0px 10px 30px; font-size: 14px; font-weight: bold; } .task-list { padding: 0px 20px; } .task-list.dx-state-disabled { opacity: 1; } .task-list.dx-state-disabled .dx-list-item { opacity: 1; } .employeeInfo .employeeNotes { min-height: 100px; padding: 20px; text-align: justify; white-space: normal; } .dx-theme-generic .dx-tabs-wrapper .dx-tab { padding-left: 14px; padding-right: 9px; line-height: 1.618; } .dx-theme-generic .dx-icon-close { font-size: 15px; } .dx-theme-material .dx-tab-content span { vertical-align: middle; line-height: initial; } .dx-theme-material .employeeNotes { margin: 0; }
var employees = [{ "ID": 1, "FirstName": "John", "LastName": "Heart", "Prefix": "Mr.", "Position": "CEO", "Picture": "../../../../images/employees/01.png", "BirthDate": "1964/03/16", "HireDate": "1995/01/15", "Notes": "John has been in the Audio/Video industry since 1990. He has led DevAv as its CEO since 2003. When not working hard as the CEO, John loves to golf and bowl. He once bowled a perfect game of 300.", "Address": "351 S Hill St.", "State": "California", "City": "Los Angeles" }, { "ID": 2, "FirstName": "Olivia", "LastName": "Peyton", "Prefix": "Mrs.", "Position": "Sales Assistant", "Picture": "../../../../images/employees/09.png", "BirthDate": "1981/06/03", "HireDate": "2012/05/14", "Notes": "Olivia loves to sell. She has been selling DevAV products since 2012. Olivia was homecoming queen in high school. She is expecting her first child in 6 months. Good Luck Olivia.", "Address": "807 W Paseo Del Mar", "State": "California", "City": "Los Angeles" }, { "ID": 3, "FirstName": "Robert", "LastName": "Reagan", "Prefix": "Mr.", "Position": "CMO", "Picture": "../../../../images/employees/03.png", "BirthDate": "1974/09/07", "HireDate": "2002/11/08", "Notes": "Robert was recently voted the CMO of the year by CMO Magazine. He is a proud member of the DevAV Management Team. Robert is a championship BBQ chef, so when you get the chance ask him for his secret recipe.", "Address": "4 Westmoreland Pl.", "State": "Arkansas", "City": "Bentonville" }, { "ID": 4, "FirstName": "Greta", "LastName": "Sims", "Prefix": "Ms.", "Position": "HR Manager", "Picture": "../../../../images/employees/04.png", "BirthDate": "1977/11/22", "HireDate": "1998/04/23", "Notes": "Greta has been DevAV's HR Manager since 2003. She joined DevAV from Sonee Corp. Greta is currently training for the NYC marathon. Her best marathon time is 4 hours. Go Greta.", "Address": "1700 S Grandview Dr.", "State": "Georgia", "City": "Atlanta" }, { "ID": 5, "FirstName": "Brett", "LastName": "Wade", "Prefix": "Mr.", "Position": "IT Manager", "Picture": "../../../../images/employees/05.png", "BirthDate": "1968/12/01", "HireDate": "2009/03/06", "Notes": "Brett came to DevAv from Microsoft and has led our IT department since 2012. When he is not working hard for DevAV, he coaches Little League (he was a high school pitcher).", "Address": "1120 Old Mill Rd.", "State": "Idaho", "City": "Boise" }, { "ID": 6, "FirstName": "Sandra", "LastName": "Johnson", "Prefix": "Mrs.", "Position": "Controller", "Picture": "../../../../images/employees/06.png", "BirthDate": "1974/11/15", "HireDate": "2005/05/11", "Notes": "Sandra is a CPA and has been our controller since 2008. She loves to interact with staff so if you've not met her, be certain to say hi. Sandra has 2 daughters both of whom are accomplished gymnasts.", "Address": "4600 N Virginia Rd.", "State": "Utah", "City": "Beaver" }, { "ID": 7, "FirstName": "Kevin", "LastName": "Carter", "Prefix": "Mr.", "Position": "Shipping Manager", "Picture": "../../../../images/employees/07.png", "BirthDate": "1978/01/09", "HireDate": "2009/08/11", "Notes": "Kevin is our hard-working shipping manager and has been helping that department work like clockwork for 18 months. When not in the office, he is usually on the basketball court playing pick-up games.", "Address": "424 N Main St.", "State": "California", "City": "San Diego" }, { "ID": 8, "FirstName": "Cynthia", "LastName": "Stanwick", "Prefix": "Ms.", "Position": "HR Assistant", "Picture": "../../../../images/employees/08.png", "BirthDate": "1985/06/05", "HireDate": "2008/03/24", "Notes": "Cindy joined us in 2008 and has been in the HR department for 2 years. She was recently awarded employee of the month. Way to go Cindy!", "Address": "2211 Bonita Dr.", "State": "Arkansas", "City": "Little Rock" }, { "ID": 9, "FirstName": "Kent", "LastName": "Samuelson", "Prefix": "Dr.", "Position": "Ombudsman", "Picture": "../../../../images/employees/02.png", "BirthDate": "1972/09/11", "HireDate": "2009/04/22", "Notes": "As our ombudsman, Kent is on the front-lines solving customer problems and helping our partners address issues out in the field. He is a classically trained musician and is a member of the Chamber Orchestra.", "Address": "12100 Mora Dr", "State": "Missouri", "City": "St. Louis" }]; var tasks = [{ "ID": 1, "Subject": "Prepare 2013 Financial", "StartDate": "2013/01/15", "DueDate": "2013/01/31", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 8 }, { "ID": 2, "Subject": "Prepare 3013 Marketing Plan", "StartDate": "2013/01/01", "DueDate": "2013/01/31", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 5 }, { "ID": 3, "Subject": "Update Personnel Files", "StartDate": "2013/02/03", "DueDate": "2013/02/28", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 2 }, { "ID": 4, "Subject": "Review Health Insurance Options Under the Affordable Care Act", "StartDate": "2013/02/12", "DueDate": "2013/04/25", "Status": "In Progress", "Priority": "High", "Completion": 50, "EmployeeID": 2 }, { "ID": 5, "Subject": "Choose between PPO and HMO Health Plan", "StartDate": "2013/02/15", "DueDate": "2013/04/15", "Status": "In Progress", "Priority": "High", "Completion": 75, "EmployeeID": 1 }, { "ID": 6, "Subject": "Google AdWords Strategy", "StartDate": "2013/02/16", "DueDate": "2013/02/28", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 1 }, { "ID": 7, "Subject": "New Brochures", "StartDate": "2013/02/17", "DueDate": "2013/02/24", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 1 }, { "ID": 11, "Subject": "Rollout of New Website and Marketing Brochures", "StartDate": "2013/02/20", "DueDate": "2013/02/28", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 5 }, { "ID": 12, "Subject": "Update Sales Strategy Documents", "StartDate": "2013/02/20", "DueDate": "2013/02/22", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 9 }, { "ID": 15, "Subject": "Review 2012 Sales Report and Approve 2013 Plans", "StartDate": "2013/02/23", "DueDate": "2013/02/28", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 5 }, { "ID": 16, "Subject": "Deliver R&D Plans for 2013", "StartDate": "2013/03/01", "DueDate": "2013/03/10", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 3 }, { "ID": 20, "Subject": "Approve Hiring of John Jeffers", "StartDate": "2013/03/02", "DueDate": "2013/03/12", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 4 }, { "ID": 20, "Subject": "Approve Hiring of John Jeffers", "StartDate": "2013/03/02", "DueDate": "2013/03/12", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 6 }, { "ID": 21, "Subject": "Non-Compete Agreements", "StartDate": "2013/03/12", "DueDate": "2013/03/14", "Status": "Completed", "Priority": "Low", "Completion": 100, "EmployeeID": 2 }, { "ID": 22, "Subject": "Update NDA Agreement", "StartDate": "2013/03/14", "DueDate": "2013/03/16", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 1 }, { "ID": 23, "Subject": "Update Employee Files with New NDA", "StartDate": "2013/03/16", "DueDate": "2013/03/26", "Status": "Need Assistance", "Priority": "Normal", "Completion": 90, "EmployeeID": 4 }, { "ID": 24, "Subject": "Update Employee Files with New NDA", "StartDate": "2013/03/16", "DueDate": "2013/03/26", "Status": "Need Assistance", "Priority": "Normal", "Completion": 90, "EmployeeID": 6 }, { "ID": 25, "Subject": "Sign Updated NDA", "StartDate": "2013/03/20", "DueDate": "2013/03/25", "Status": "Completed", "Priority": "Urgent", "Completion": 100, "EmployeeID": 7 }, { "ID": 26, "Subject": "Sign Updated NDA", "StartDate": "2013/03/20", "DueDate": "2013/03/25", "Status": "Completed", "Priority": "Urgent", "Completion": 100, "EmployeeID": 8 }, { "ID": 27, "Subject": "Sign Updated NDA", "StartDate": "2013/03/20", "DueDate": "2013/03/25", "Status": "Need Assistance", "Priority": "Urgent", "Completion": 25, "EmployeeID": 9 }, { "ID": 35, "Subject": "Update Revenue Projections", "StartDate": "2013/03/24", "DueDate": "2013/04/07", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 8 }, { "ID": 36, "Subject": "Review Revenue Projections", "StartDate": "2013/03/25", "DueDate": "2013/04/06", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 9 }, { "ID": 40, "Subject": "Provide New Health Insurance Docs", "StartDate": "2013/03/28", "DueDate": "2013/04/07", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 4 }, { "ID": 41, "Subject": "Provide New Health Insurance Docs", "StartDate": "2013/03/28", "DueDate": "2013/04/07", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 6 }, { "ID": 50, "Subject": "Give Final Approval for Refunds", "StartDate": "2013/05/05", "DueDate": "2013/05/15", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 2 }, { "ID": 52, "Subject": "Review Product Recall Report by Engineering Team", "StartDate": "2013/05/17", "DueDate": "2013/05/20", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 1 }, { "ID": 55, "Subject": "Review Overtime Report", "StartDate": "2013/06/10", "DueDate": "2013/06/14", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 7 }, { "ID": 60, "Subject": "Refund Request Template", "StartDate": "2013/06/17", "DueDate": "2014/04/01", "Status": "Deferred", "Priority": "Normal", "Completion": 0, "EmployeeID": 9 }, { "ID": 71, "Subject": "Upgrade Server Hardware", "StartDate": "2013/07/22", "DueDate": "2013/07/31", "Status": "Completed", "Priority": "Urgent", "Completion": 100, "EmployeeID": 7 }, { "ID": 72, "Subject": "Upgrade Personal Computers", "StartDate": "2013/07/24", "DueDate": "2014/04/30", "Status": "In Progress", "Priority": "Normal", "Completion": 85, "EmployeeID": 7 }, { "ID": 74, "Subject": "Decide on Mobile Devices to Use in the Field", "StartDate": "2013/07/30", "DueDate": "2013/08/02", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 3 }, { "ID": 78, "Subject": "Try New Touch-Enabled WinForms Apps", "StartDate": "2013/08/11", "DueDate": "2013/08/15", "Status": "Completed", "Priority": "Normal", "Completion": 100, "EmployeeID": 3 }, { "ID": 81, "Subject": "Review Site Up-Time Report", "StartDate": "2013/08/24", "DueDate": "2013/08/30", "Status": "Completed", "Priority": "Urgent", "Completion": 100, "EmployeeID": 5 }, { "ID": 99, "Subject": "Submit D&B Number to ISP for Credit Approval", "StartDate": "2013/11/04", "DueDate": "2013/11/07", "Status": "Completed", "Priority": "High", "Completion": 100, "EmployeeID": 8 }, { "ID": 117, "Subject": "Approval on Converting to New HDMI Specification", "StartDate": "2014/01/11", "DueDate": "2014/01/31", "Status": "Deferred", "Priority": "Normal", "Completion": 75, "EmployeeID": 3 }, { "ID": 138, "Subject": "Review HR Budget Company Wide", "StartDate": "2014/03/20", "DueDate": "2014/03/25", "Status": "In Progress", "Priority": "Normal", "Completion": 40, "EmployeeID": 6 }, { "ID": 145, "Subject": "Final Budget Review", "StartDate": "2014/03/26", "DueDate": "2014/03/27", "Status": "In Progress", "Priority": "High", "Completion": 25, "EmployeeID": 6 }];