Versions: SharePoint 2013
Definitions: SharePoint custom list, SharePoint list GUID, SharePoint list item, SharePoint field, SharePoint field internal name
References:
Tags: #JavaScript, #JQuery, #SharePoint
A huge function that separates and rearranges the fields in a Display form (of a list that have a lot of fields) into tabs.

// Steps before using the function :
// [1] - Create a SharePoint custom list for administration with 3 fields : 
// 1. - text field                   : "List GUID" for example
// 2. - checkbox field               : "Apply theme"
// 3. - multiple lines of text field : "Config"
// [2] - Create a list item, fill the create form with :
// 1. - "List GUID"   : GUID of the target business custom list which the display form to stylize belongs (see How to get list GUID(link))
// 2. - "Apply theme" : Yes/No to apply or not the theme/style to the display form of the target list
// 3. - "Config"      : JSON string that contains the names of tabs to create and theirs fields.
// [3] - See 'Admin list and JSON config' below for better understanding

/**
 * Separates and rearranges the fields of a Disp form (of a list) into tabs.
 * - this function need to be loaded with the display form to stylize
 * - very useful with lists that have a lot of fields
 * @requires jQuery;jQueryUI
 * @param {string} adminListName - Name of the 'administration list' (created)
 * @param {string} listGuidField - Internal name of the "List GUID" field in the 'administration list' (see How to get field internal name(link))
 * @param {string} applyThemeField - Internal name of the field "Apply theme"
 * @param {string} configField - Internal name of the field "Config"
 */

// Set administration list's informations here (needed in the HTTP get request)
var adminListName = "";
var listGuidField = "";
var applyThemeField = "";
var configField = "";

(function tabsForForms(adminListName, listGuidField, applyThemeField, configField)
{
    // Vars
    var currentListGuid = _spPageContextInfo.pageListId;  // GUID of the current list used to get associated item in the administration list if any !
    var appTheme = false;                                                
    var configInfos = {};                           
    var itemId = 0;                                 
    
    var tabsNames = []; // for creating HTML elements
    var tabFields = []; // to search fields' HTML elements and attach them to "their" tabs (as indicated in the user's config)
    var htmlTabs = "";
    var i = 0;

    // // You would have notice that not the entire code is inside $(document).ready() because for perf is better to avoid this when not needed

    // looking for our item in the administration list. Our item contains the user's configuration infos needed, target list and whether or not he would to apply the style . 
    // 1 - Get all the items of the administration list, we use the list's title
    $.ajax({
        url: _spPageContextInfo.webAbsoluteUrl + "/_api/web/lists/getbytitle(\'"+ adminListName +"\')/items",
        method: "GET",
        headers: { "Accept" : "application/json; odata=verbose" },
        success: onSuccess,
        error: function (data) {}
    });

    function onSuccess(data) // put all the code to exec in a callback function (async) 
    {   
        // 2 - Get the item's informations from the the administration list
        var items = data.d.results;
        for(var i=0; i < items.length; i++)
        {
            if( currentListGuid == items[i][listGuidField] || currentListGuid.toUpperCase() === items[i][listGuidField] || currentListGuid.replace(/{|}/g,"") === items[i][listGuidField] || currentListGuid.toUpperCase().replace(/{|}/g,"") === items[i][listGuidField] )
            {
                itemId = items[i]['ID']; // not really needed
                appTheme = items[i][applyThemeField]; // user's Y/N for applying the theme/style
                configInfos = JSON.parse(items[i][configField]); // parsing user's configuration infos
                //return;
                i = items.length;
            } 
        }
    
        if(appTheme) // check if the user set ON for applying the theme/style
        {
            // for tabs names
            tabsNames = configInfos.onglets; //or configInfos['onglets']
            
            // for tab's fields
            tabFields = configInfos.champs;
    
            // creating HTML elements to use for tabs
            htmlTabs = "<div id='tabs'><ul style='font-size:12px'>";
    
            for(i = 0; i < tabsNames.length; i++)
            {
                htmlTabs += "<li><a href=\'#tabs-" + (i+1) + "\'>" + tabsNames[i] + "</a></li>";
            }
    
            htmlTabs += "</ul>";
    
            for(i = 0; i < tabsNames.length; i++)
            {
                htmlTabs += "<div><table cellpadding='0' cellspacing='0' id='tabs-"+ (i+1) +"'><tbody style='display: table; width:100% !important;'><tr><td style='vertical-align: top; width:50%'><table style='display:inline-block;'></table></td><td style='vertical-align: top; width:50%'><table style='display:inline-block'></table></td></tr></tbody></table></div>";
            }
    
            htmlTabs += "</div>";
    
            // optional : display the fields on two columns in the tab, it's not 100% accurate (see Comments [1].1 below)
            // 1 - init arrays (counters)
            var counter = [tabsNames];
            var meter = [tabsNames];
            for(i=0; i < tabsNames.length; i++)
            {
                counter[i] = 0;
                meter[i] = 0;
            }
            // 2 - divide the fields 
            for(i=0; i < tabFields.length; i++)
            {
                for(var j = 0; j < tabsNames.length; j++)
                {   
                    if(parseInt(tabFields[i].split("|")[0]) == j+1) { counter[j] ++; }
                }
            }
    
            // // This section need to be inside $(document).ready()
    
            $(document).ready(function(){
                
                // adding the HTML elements created to the DOM and calling the JQueryUI .tabs() function
                var slc = ".ms-formtable"; //or = "#WebPartWPQ2 > table > tbody > tr:nth-child(3) > td > table > tbody"; // see Comments [2].1 below
                $(htmlTabs).insertAfter(slc).tabs();
    
                // array of fields' names
                var arrTab = tabFields;
    
                // call a function getFieldsByIN
                var fields = getFieldsByIN();
    
                // put the fields in the correct tab (and on two columns) : the sizes of the two columns are not equal but auto to include the case of the blocks of 'Comments'
                var select;
                var currField;
                $.each(arrTab, function(idx,item)
                {
                    var split = item.split('|');
                    var tabID = split[0];
                    var fieldName = split[1];
                    if(fields[fieldName] != undefined)
                    {   
                        currField = $(fields[fieldName]);
                        if(meter[tabID-1] < ( counter[tabID-1] / 2 ) ) { select = $($('#tabs-'+tabID).find('table')[0]); } else { select = $($('#tabs-'+tabID).find('table')[1]); }
                        currField.appendTo(select);
                        meter[tabID-1] ++;
                    }
                    else
                    {
                        // see Comments [1].2 below
                        counter[tabID-1] --; 
                    }
                });
    
                // if there is an "Attachments" field, put it in the last tab
                //$("#idAttachmentsRow").appendTo('#tabs-'+ (tabsNames.length)).find('.ms-formlabel').attr('width','165px');
            });
        }
    }

    // // Utility functions 
    // Gets fields (tr elements of the form) by their internal names (more robust) and creates/returns an "object"
    function getFieldsByIN()
    {
        var res = {};
        $("td.ms-formbody").each(function()
        {   
            if($(this).html().indexOf('FieldInternalName="') > -1)
            {   
                if($(this.parentNode).css('display') != 'none')
                {
                    var str = $(this).html().indexOf('FieldInternalName="')+19;
                    var stp = $(this).html().indexOf('FieldType="')-6;
                    var fin = $(this).html().substring(str,stp);
                    res[fin] = this.parentNode;
                }
            }
        });
        return res;
    }
    
    // Gets fields (tr elements of the form) by their display names (see Comments [2].2 below) and creates/returns an "object"
    /*
    function getFieldsByDN(){
        var res = {};
        $("h3.ms-standardheader").each(function(){
            var fdn = $(this).text();
            res[fdn] = $(this).parents(':eq(1)');
        });
        return res;
    }
    */

})(adminListName, listGuidField, applyThemeField, configField); 

// Comments:

// [1] - Configuration in JSON format
// 1. - the field is assigned to a tab based on the configuration (tabIndex[field) and not its "order" in the array in the JSON config infos 
// 2. - cases :
// . case where the fields are passed in the config infos but not displayed/not existing in the DOM
// . like [1].1 : approximate solution because the decrementation is done parallel to the assignment of the fields, because of the grouping (tabs) where we do not know beforehand the complete number of the fields in a group (error in the config, order not necessary respected)
// However, the calculation errors are very subtle and not remarkable, it is possible to make a more complete verification but this may penalize the performance.

// [2] - in case of user created forms where : 
// .1 - there is no .ms-formtable class 
// .2 - there are no internal names 

// Admin list and JSON config:
// [1] - List GUID field accepts those (examples) format : 
// . {40c34602-d8b2-4fae-9f48-a1c96d27dd25} or
// . {40C34602-D8B2-4FAE-9F48-A1C96D27DD25} or
// . 40c34602-d8b2-4fae-9f48-a1c96d27dd25 or
// . 40C34602-D8B2-4FAE-9F48-A1C96D27DD25 or
// [2] - Config field contains JSON string as follow :
// {
//     "onglets": [
//       "tab 1 name",
//       "tab 2 name",
//       "tab 3 name"
//     ],
//     "champs": [
//       "tab index|field internal name",
//       "tab index|field internal name",
//       "tab index|field internal name",
//       "tab index|field internal name",
//       "tab index|field internal name",
//       "tab index|field internal name",
//     ]
// }
// . no order in "champs" array in the JSON string, but it's recommended to keep things organized
// . note that '|' symbol is used to separate a field and his tab, there is no risk to find this symol in an internal name