I've been trying to get Tippy.js' nested tooltip feature working but haven't had luck so far.
the following lines are placed outside jQuery's Document ready function:
tippy.setDefaultProps({
appendTo: function () {
return document.querySelector('.responseBody')
}
})
popperOptions: { strategy: 'fixed' };
tippy.delegate('.responseBody', {
target: '.tooltip'
});
the tooltip content is being added dynamically based on an extract from ajax response and the ajax function is the following:
function _loadLaunchPad(iSMHost, iSMPort, urlExtension) {
var _serverName = $('#serverName option:selected').text();
var _addOnSelect = $('#addOnSelect option:selected').text();
var _consoleCmdExprValue = $('#consoleCmdExprValue').val();
var dataString = '{ "serverSelection" : "' + _serverName + '", "operationSelection" : "' + _addOnSelect + '", "commandSelection" : "' + _consoleCmdExprValue + '"}';
var stringToSend = " <img class='preLoader' src='images/preloader.gif' title = 'Processing request'/>";
const _toolTipContentHeader = ' <div>' +
' <strong>Server Name <span class="tooltip" data-title="<strong>hey</strong>" style="color: aqua">%HDRITEM0%</span></strong><br />' +
' <strong>Server IP Address <span style="color: aqua">%HDRITEM1%</span></strong><br />' +
' <strong>Server #Processors <span style="color: aqua">%HDRITEM2%</span></strong><br />' +
' <strong>Version <span style="color: aqua">%HDRITEM3_1%</span></strong><br />' +
' <strong>Build Ref <span style="color: aqua">%HDRITEM3_2%</span></strong><br />' +
// ' <strong>Build Date <span style="color: aqua">%HDRITEM3_3%</span></strong><br />' +
' <strong>Configs Count <span style="color: aqua">%HDRITEM4%</span></strong><br />' +
' <strong>Master Config <span style="color: aqua">%HDRITEM5%</span></strong><br />' +
' </div>';
const _toolTipContentLine = ' <div>' +
' <strong>Server Name <span style="color: aqua">%LNITEM0%</span></strong><br />' +
' <strong>Config Process ID <span style="color: aqua">%LNITEM1%</span></strong><br />' +
' <strong>Config Status <span style="color: aqua">%LNITEM2%</span></strong><br />' +
' <strong>Config Java Version <span style="color: aqua">%LNITEM3%</span></strong><br />' +
' <strong>Version <span style="color: aqua">%LNITEM4_1%</span></strong><br />' +
' <strong>Build Ref <span style="color: aqua">%LNITEM4_2%</span></strong><br />' +
// ' <strong>Build Date <span style="color: aqua">%LNITEM4_3%</span></strong><br />' +
' <strong>iBSE Port (If Enabled) <span style="color: aqua">%LNITEM5%</span></strong><br />' +
' <strong>Master Config <span style="color: aqua">%LNITEM6%</span></strong><br />' +
' <strong>Registry <span style="color: aqua">Here</span></strong><br />' +
' <strong>Blue <span style="color: aqua">Here</span></strong><br />' +
' <strong>Runtime <span style="color: aqua">Here</span></strong><br />' +
' </div>';
$.ajax({
type: "POST",
url: `http://${iSMHost}:${iSMPort}/${urlExtension}`,
data: dataString,
timeout: 60000 * 30, //60000 milliseconds * 30 = 30 minutes
async: true,
contentType: "application/json",
cache: false,
beforeSend: function () {
$(".terminal .top").addClass('color-change-5x');
$('.terminal .responseBody').html("");
},
success: function (result) {
if (_addOnSelect == 'ConfigsList') {
var h1 = $('<h1 class="responseTitleText">Launch Pad</h1>')
var tabDiv = $('<div id="tabsID"></div>')
var prodTab = $('<div class="tab-container" data-tab-index="0"></div>');
var testTab = $('<div class="tab-container" data-tab-index="1" style="display:none;"></div>');
$('.terminal .responseBody').append(h1);
$('.terminal .responseBody').append(prodTab);
$('.terminal .responseBody').append(testTab);
for (var i = 0; i < result.length; ++i) {
var div = $('<div>'), ulForTest = $('<ul>'), ulForProd = $('<ul>');
var _tempHeaderItem = result[i];
var _tempInstanceFlag = (_tempHeaderItem.host).split(':')[1];
var _tempHostEnvironment = (_tempHeaderItem.host).split(':')[0]
_tempHostEnvironment = ((/p.me.com$/).test(_tempHostEnvironment)) ? 'Prod' : 'Test';
var replacementsForHDR = {
"%HDRITEM0%": _nvl(_tempHeaderItem.host.replace('.me.com', '')),
"%HDRITEM1%": _nvl(_tempHeaderItem.serverIP),
"%HDRITEM2%": _nvl(_tempHeaderItem.processorsCount),
"%HDRITEM3_1%": _nvl(_tempHeaderItem.configs[0].Version),
"%HDRITEM3_2%": _nvl(_tempHeaderItem.configs[0].Build),
// "%HDRITEM3_3%": _nvl(_tempHeaderItem.configs[0].BuildDate),
"%HDRITEM4%": _nvl(('Active: ' + _tempHeaderItem.activeConfigsCount + '/ Inactive: ' + _tempHeaderItem.inActiveConfigsCount)),
"%HDRITEM5%": _nvl(_tempHeaderItem.master)
};
var _toolTipContentHeaderInstance = _toolTipContentHeader.replace(/%\w+%/g, function (all) {
return replacementsForHDR[all] || all;
});
_tempInstanceFlag = (_tempInstanceFlag == '19') ? '1st' : (_tempInstanceFlag == '29') ? '2nd' : (_tempInstanceFlag == '39') ? '3rd' : (_tempInstanceFlag == '49') ? '4th' : '1st?';
if(_tempHostEnvironment == 'Prod'){
ulForProd.append("<li class='tooltip' data-title='" + _toolTipContentHeaderInstance + "'>" + ((_tempHeaderItem.host).split('.')[0]).replace(".me.com", "") + " " + _tempInstanceFlag + "</li>")
}else{
ulForTest.append("<li class='tooltip' data-title='" + _toolTipContentHeaderInstance + "'>" + ((_tempHeaderItem.host).split('.')[0]).replace(".me.com", "") + " " + _tempInstanceFlag + "</li>")
}
for (var j = 0; j < _tempHeaderItem.configs.length; ++j) {
var _tempLineItem = _tempHeaderItem.configs[j];
var replacementsForLN = {
"%LNITEM0%": _nvl(_tempHeaderItem.host.replace('.me.com', '')),
"%LNITEM1%": _nvl(_tempLineItem.processID),
"%LNITEM2%": _nvl(_tempLineItem.status),
"%LNITEM3%": _nvl(_tempLineItem.javaVersion),
"%LNITEM4_1%": _nvl(_tempLineItem.Version),
"%LNITEM4_2%": _nvl(_tempLineItem.Build),
// "%LNITEM4_3%": _nvl(_tempLineItem.BuildDate),
"%LNITEM5%": _nvl(((_tempLineItem.ibsePort.length > 0) ? _tempLineItem.ibsePort : 'NA')),
"%LNITEM6%": _nvl(_tempHeaderItem.master),
"%LNITEM7%": _nvl('http://' + _tempHeaderItem.host + '/' + _tempLineItem.config + '&filter=on'),
"%LNITEM8%": _nvl('http://' + _tempHeaderItem.host + '/yes?configuration=' + _tempLineItem.config),
"%LNITEM9%": _nvl('http://' + _tempHeaderItem.host + '/yes?configuration%7D_' + _tempLineItem.config)
};
var _toolTipContentLineInstance = _toolTipContentLine.replace(/%\w+%/g, function (all) {
return replacementsForLN[all] || all;
});
if(_tempHostEnvironment == 'Prod'){
div.append(ulForProd.append("<li class='tooltip' data-title='" + _toolTipContentLineInstance + "'>" + _tempLineItem.config + "</li>"));
$('#tabLink').show();
$('.terminal .responseBody .tab-container[data-tab-index=0]').append(div.addClass('tracking-in-expand-fwd'));
}else{
div.append(ulForTest.append("<li class='tooltip' data-title='" + _toolTipContentLineInstance + "'>" + _tempLineItem.config + "</li>"));
$('#tabLink').show();
$('.terminal .responseBody .tab-container[data-tab-index=1]').append(div.addClass('tracking-in-expand-fwd'));
}
}
}
}
tippy('.tooltip', {
theme: 'custom',
content: function (reference) {
const htmlContent = reference.getAttribute('data-title');
return htmlContent;
},
interactive: true,
allowHTML: true,
multiple: true
});
popperOptions: { strategy: 'fixed' };
},
error: function (x, t, m) {
$('.terminal .responseBody').html("<p class='error fade-in-fwd'>Something went wrong at the server. Thats all I know! :-(</p>");
},
complete: function (x, t, m) {
$('.terminal .title').html('response pane');
$(".terminal .top").removeClass('color-change-5x');
}
});
return false;
}
the relevant CSS styling info
.tippy-box[data-theme~="custom"] span {
float: right;
}
.tippy-box[data-theme~="custom"] {
width: max-content;
}
Owing to the complexity of the application, I couldn't create a repro on a fiddle/etc. The first tooltip gets displayed well, while the nested does not get displayed.
result:
Any help would be much appreciated.
If the nested tooltip is invoked using data-tippy-content with html, the value gets displayed but the HTML gets displayed raw, meaning, HTML tags appear as-is.
I'm trying to iterate through JSON data and populate the content dynamically using jQuery.
First I do an Ajax request
Then I loop through the data and create the HTML tags dynamically
I create a div container with a class of "product-wrapper" for each JSON object (in this case 2 divs product-wrapper). As you can see on the example of my JSON data, each object has an array of objects "details".
From my second foreach loop - how can I populate the data of "details" within its appropriate "product-wrapper" div?
JavaScript
myApp.init = function(){
var rates = {
products: $('.products-container'),
init: function(){
rates.showRates();
},
showRates: function(){
$.each(utils.rates, function(index, value){
rates.products.append(
'<div class="product-wrapper">' +
'<div class="product-heading">' + value.name + '</div>' +
'<div class="product-details"></div>' +
'</div>'
);
$.each(value.details, function(i,val){
$('.product-details').append('<div class="detail-row">' +
'<div class="detail detail-balance">' + val.balance + '</div>' +
'<div class="detail detail-gross-rate">' + val.grossRate + '</div>' +
'<div class="detail detail-aer">' + val.aer + '</div>' +
'</div>');
});
});
}
};
var utils = {
rates: '',
url: $('.business-interest-rates').attr('data-rates-json'),
init: function(){
utils.ajaxRequest();
},
ajaxRequest: function(){
$.ajax({
url: utils.url,
dataType: 'json',
success:function(data){
utils.rates = data;
if($('#interest-rates').length > 0){
rates.init();
}
},
error: function(e){
console.log(e.message);
}
});
}
};
utils.init();
};
JSON
[
{
"name":"Australian dollar (AUD)",
"currency":"AUD",
"details":[
{
"balance": "1+",
"grossRate": "1.700",
"aer": "0.5"
},
{
"balance": "500,000+",
"grossRate": "2.100",
"aer": "2.00"
},
{
"balance": "1,500,000+",
"grossRate": "2.450",
"aer": "1.00"
}
]
},
{
"name":"EURO (EU)",
"currency":"EU",
"details":[
{
"balance": "1+",
"grossRate": "1.700",
"aer": "1.50"
},
{
"balance": "500,000+",
"grossRate": "2.100",
"aer": "2.20"
}
]
}
]
Either don't use a CSS class to append your stuff but unique ids, or first generate the whole HTML code (wrapper and details all together) in one step and then append it.
// use unique IDs for appending
function () {
$.each(utils.rates, function (index, value) {
rates.products.append(
'<div class="product-wrapper">' +
'<div class="product-heading">' + value.name + '</div>' +
'<div id="product-details-' + value.name + '"></div>' +
'</div>'
);
$.each(value.details, function (i, val) {
$('#product-details-' + value.name).append('<div class="detail-row">' +
'<div class="detail detail-balance">' + val.balance + '</div>' +
'<div class="detail detail-gross-rate">' + val.grossRate + '</div>' +
'<div class="detail detail-aer">' + val.aer + '</div>' +
'</div>');
});
});
}
// first generate all html code, then append
function () {
$.each(utils.rates, function (index, value) {
var html = '<div class="product-wrapper">' +
'<div class="product-heading">' + value.name + '</div>' +
'<div class="product-details">';
$.each(value.details, function (i, val) {
html += '<div class="detail-row">' +
'<div class="detail detail-balance">' + val.balance + '</div>' +
'<div class="detail detail-gross-rate">' + val.grossRate + '</div>' +
'<div class="detail detail-aer">' + val.aer + '</div>' +
'</div>';
});
html += '</div>' +
'</div>';
rates.products.append(
html
);
});
}
This is how I am creating my table & rendering into the aspx page after getting the data from from database by ajax call. I checked the stored procedure execution Time which is only 1.5 seconds where table in my page renders about in 7- 8 seconds. I need to optimize the speed. Any suggestion how I can do it?
$.ajax({
method: 'POST',
//async: false,
data: json_data,
dataType: 'json',
url: json_url
}).success(function (response) {
if (response.IsError) {
showError('Did not receive values from XRM.');
} else {
var webResponse = response;
var dataSet = webResponse["Orders"];
var statuses = [];
//console.log(dataSet);
if (table_role == 'pending') {
statuses = [
{
'id': 39,
'name': 'Pending Scheduling',
'class': 'schedule',
'color': '#7C44BC',
'order': 0
},
{
'id': 40,
'name': 'Pending Screening',
'class': 'screening',
'color': '#8e8e00',
'order': 1
},
{
'id': 41,
'name': 'Pending Pre-Auth',
'class': 'preauth',
'color': '#cc8500',
'order': 2
},
{
'id': 42,
'name': 'Ready For Confirmation',
'class': 'confirm',
'color': '#cc0070',
'order': 3
},
{
'id': 1,
'name': 'Open',
'class': 'open',
'color': null,
'order': 4
},
{
'id': 2,
'name': 'New Order',
'class': 'neworder',
'color': null,
'order': 5
},
{
'id': 3,
'name': 'Changed',
'class': 'changed',
'color': '#cd4e3e',
'order': 6
},
{
'id': 5,
'name': 'Approved',
'class': 'approved',
'color': '#6aa745',
'order': 7
},
{
'id': 6,
'name': 'On Hold',
'class': 'onhold',
'color': '#009acd',
'order': 8
},
{
'id': 11,
'name': 'In Queue',
'class': 'queue',
'color': null,
'order': 9
}
];
// For customers, change labels
if (is_customer) {
statuses[5]['name'] = 'Submitted to SMS';
statuses[9]['name'] = 'Approved';
}
} else if (table_role == 'preauth') {
statuses = [
{
'id': 29,
'name': 'More Info Needed',
'class': 'moreinfo',
'order': 0
},
{
'id': 43,
'name': 'Phone Order',
'class': 'phone',
'order': 1
},
{
'id': 30,
'name': 'Received Order',
'class': 'received',
'order': 2
},
{
'id': 31,
'name': 'Waiting on Documentation',
'class': 'waiting',
'order': 3
},
{
'id': 32,
'name': 'Courtesy Call Pending',
'class': 'courtesy-pending',
'order': 4
},
{
'id': 33,
'name': 'Courtesy Call Complete',
'class': 'courtesy-complete',
'order': 5
},
{
'id': 34,
'name': 'In Pre-Authorization',
'class': 'preauth',
'order': 6
},
{
'id': 35,
'name': 'Additional Docs Needed',
'class': 'addtldocs',
'order': 7
},
{
'id': 36,
'name': 'Pending Authorization',
'class': 'pending',
'order': 8
}
];
}
// Remove any "sorting" backgrounds from headers
$th.removeClass('otable__sort');
// If no orders available
if ($.isEmptyObject(dataSet)) {
$table.find('.otable__empty').css('display', 'table-cell');
} else {
// Loop through orders
$.each(dataSet, function (key, value) {
if (table_role == 'pending') {
var id = (value['ID'] == null) ? '' : value['ID'],
date = (value['ID'] == null) ? '' : value['ExamDate'],
billing = (value['BillCode'] == null) ? '' : value['BillCode'],
scanner = (value['ScannerCode'] == null) ? '' : value['ScannerCode'],
units = (value['Unit'] == null) ? '' : value['Unit'],
by = (value['EmployeeName'] == null) ? '' : value['EmployeeName'],
status = value['StatusID'],
customer = value['Customer'],
location = '';
// Build location string
if (value['Facility']) {
location += value['Facility'] + '<br>';
}
if (value['City'] && value['State']) {
location += value['City'] + ', ' + value['State'];
}
} else if (table_role == 'preauth') {
var id = (value['ID'] == null) ? '' : value['ID'],
poid = (value['POID'] == null) ? '' : value['POID'],
site = (value['Site'] == null) ? '' : value['Site'],
patient = (value['PatientName'] == null) ? '' : value['PatientName'],
insurance_co = (value['InsuranceCo'] == null) ? '' : value['InsuranceCo'],
received_date = (value['ReceivedDate'] == null) ? '' : value['ReceivedDate'],
exam_date = (value['ExamDate'] == null) ? '' : value['ExamDate'],
updated_date = (value['UpdatedDate'] == null) ? '' : value['UpdatedDate'],
status = value['StatusID'],
priority = (value['Priority'] == null) ? 0 : value['Priority'],
custid = (value['custID'] == null) ? '' : value['custID'];
}
// Find status from ID
var object = $.grep(statuses, function (element, index) {
return element['id'] == status;
});
object = object[0];
// Get status name
var status_name = object['name'],
status_pos = object['order'],
status_class = object['class'],
datestring = '',
received_datestring = '',
exam_datestring = '',
updated_datestring = '';
// Build date strings
if (date) {
datestring = splitDate(date);
}
if (received_date) {
received_datestring = splitDate(received_date);
}
if (exam_date) {
exam_datestring = splitDate(exam_date);
}
if (updated_date) {
updated_datestring = splitDate(updated_date);
}
// Build Time strings
if (updated_date) {
updated_timestring = splitTime(updated_date);
}
else {
updated_timestring = '';
}
// Convert priority for sorting
var priority_order;
if (priority) {
if (priority == 0) {
priority_order = 2;
} else if (priority == 5) {
priority_order = 1;
} else if (priority == 10) {
priority_order = 0;
}
}
// HTML for table row
var $row;
if (table_role == 'pending') {
if (!is_customer && (is_scheduler || is_retail)) {
$row = $('<tr class="otable__row otable__row--' + status_class + '">' +
'<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
'<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
'<td class="otable__billing"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + billing + '</a></td>' +
'<td class="otable__scanner"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + scanner + '</a></td>' +
'<td class="otable__loc"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + location + '</a></td>' +
'<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
'<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
'<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
'</tr>');
} else if (!is_customer && (!is_scheduler || !is_retail)) {
$row = $('<tr class="otable__row otable__row--">' +
'<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
'<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
'<td class="otable__billing"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + billing + '</a></td>' +
'<td class="otable__scanner"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + scanner + '</a></td>' +
'<td class="otable__loc"><a href=#rptPendingGroupByOrder onclick=showDialog(\'/CustomerProfile.aspx?cid=' + customer + '\')>' + location + '</a></td>' +
'<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
'<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
'<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
'</tr>');
} else {
$row = $('<tr class="otable__row otable__row--customer otable__row--' + status_class + '">' +
'<td class="otable__id"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + id + '</a></td>' +
'<td class="otable__date" data-sort-value="' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + datestring + '</a></td>' +
'<td class="otable__units"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + units + '</a></td>' +
'<td class="otable__by"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + by + '</a></td>' +
'<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + date + '"><a href=/Customer/FDGOrders.aspx?oid=' + id + '>' + status_name + '</a></td>' +
'</tr>');
}
} else if (table_role == 'preauth') {
var insurance_sort_val = priority_order;
if (exam_date && exam_date.indexOf('0001-01-01') < 0) {
insurance_sort_val = priority_order + ' ' + exam_date;
} else if (received_date) {
// Force items with null exam date to bottom, then sort by received date
insurance_sort_val = priority_order + ' Z ' + received_date;
}
$row = $('<tr class="otable__row otable__row--' + status_class + '">' +
'<td class="otable__id">' + id + '</td>' +
'<td class="otable__site">' + site + '</td>' +
'<td class="otable__by">' + patient + '</td>' +
'<td class="otable__insurance otable__priority--' + priority + '" data-sort-value="' + insurance_sort_val + '">' + insurance_co + '</td>' +
'<td class="otable__date" data-sort-value="' + received_date + '">' + received_datestring + '</td>' +
'<td class="otable__date" data-sort-value="' + exam_date + '">' + exam_datestring + '</td>' +
'<td class="otable__status" data-status-id="' + status + '" data-sort-value="' + status_pos + ' ' + exam_date + '">' + status_name + '</td>' +
'<td class="otable__date--wide" data-sort-value="' + updated_date + '">' + updated_datestring + ' ' + updated_timestring + '</td>' +
'</tr>');
$row.find('a').on('click', function (e) {
if (!$(this).parent('.otable__site').length) {
e.preventDefault();
showDialog('/Customer/FDGPatientOrder.aspx?oid=' + id + '&poid=' + poid);
}
if ($(this).parent('.otable__site').length) {
e.preventDefault();
showDialog('/CustomerProfile.aspx?cid=' + custid);
}
});
}
// Append to table body
$table.find('tbody').append($row);
});
}
// Fire event
//$table.trigger('all-rows-added');
//$table.off('all-rows-added');
// Table sorting functionality
if (table_role == 'pending') {
initTableSorting($table, 7);
} else if (table_role == 'preauth') {
initTableSorting($table, 3);
}
// Status filter functionality
var $list = $table.find('.otable__status__list');
// Clear previous filters
$list.find('ul>li:not(:first)').remove();
// Loop through statuses to see which are contained in the table
$.each(statuses, function (key, value) {
if ($table.find('tbody').has('.otable__status[data-status-id="' + value.id + '"]').length > 0) {
var $li;
if (table_role == 'pending') {
$li = $('<li>' +
'<button data-filter="' + value.id + '" data-color="' + value.color + '">' +
'<div class="otable__legend"></div>' +
value.name +
'</button>' +
'</li>');
} else {
$li = $('<li>' +
'<button data-filter="' + value.id + '">' +
'<div class="otable__legend"></div>' +
value.name +
'</button>' +
'</li>');
}
// Add the list item
$li = $li.appendTo($list.find('ul'));
// Set the legend color
var $btn = $li.find('button'),
filter = $btn.data('filter'),
color = $btn.data('color');
if (color !== null) {
$li.find('.otable__legend').css('background', color);
}
}
});
$th.on('click', function (e) {
// Check if main filter is being clicked, otherwise don't sort
if ($list.has(e.target).length > 0) {
var $target = $(e.target).closest('button'),
filter = $target.data('filter');
// Sort all rows by ascending status
$table.find('th').eq(7).stupidsort('asc');
// Style actively selected item
$list.find('li').removeClass('otable__status__checked');
$target.parent('li').addClass('otable__status__checked');
if (filter == 'all') {
$table.find('tbody > tr').css('display', 'table-row');
} else {
$table.find('tbody > tr').css('display', 'none');
$table.find('tbody > tr').has('.otable__status[data-status-id="' + filter + '"]').css('display', 'table-row');
}
// Force hide list when option clicked
$list.css({
'clip': 'rect(0 0 0 0)',
'opacity': 0
});
// Restore default behavior once mouse is moved
$table.mousemove(function () {
$list.removeAttr('style');
});
// Prevent main sorting action
return false;
}
});
// Sticky table header
$table.stickyTableHeaders();
// Remove loading indicator
$table.find('.otable__loading').css('display', 'none');
}
}).fail(function (jqxhr, textStatus, error) {
var err = textStatus + ", " + error;
console.log("Failed to get XRM initialization values. Message: " + err);
});
We ran out in similar scenarios, where we had more than 30000 records to render on the same page, API was doing good but UI became pathetic in terms of performance. Chrome and Firefox survived somehow and rendered records with delays of 10 to 15 seconds, but IE was a nightmare, where the browser just froze everytime we accessed that page. The solution to this problem was gradual but elegant, we had to switch to the basics. I would break down a subset of my solution here
Replace $.each and _.each loops with native for(var i = 0; i < lenght; i += 1) loops. Doing this created a major boost in terms of performance. To prove this concept, at that time I created a jsperf (check here) performance benchmark, which showed huge performance gains. In your case, for example, replace $.each(dataSet, function (key, value) { and $.each(statuses, function (key, value) { with a native for loops. See this image comparing the loops
Reducing the number of JQuery selectors also proved to be beneficial, and instead of creating an in memory jquery elements like $('<div>bla bla bla</div>');, use string concatenation and at the end using for example $el.html() proved to be very efficient. In your case instead of doing things like $row = $('<tr class ... and then binding function like $row.find('a').on('click', you only need to create a long string and append everything in it, and at the end append that long string to the DOM. If you are just doing this to bind the click event to every single row (OMG), you can achieve the similar behavior by using JQuery on click on the parent with child as a filter, like $('table').on('click', 'tr', myClickFunc);. This will only bind a single click event instead of thousands of click events, resulting in a great performance gain.
By avoiding JQuery filtering methods like $.grep or $.map or $.filter etc, and using native loops, also resulted in drastic improvements.
By using jQuery element caching and avoiding frequent queries made to the DOM significant gains were achieved in different pages. I think you are doing caching fine, but try avoiding frequent DOM querying.
Avoiding multiple method calling from the loops was also eating up the overall loading time, so combining multiple method calls to a single method call and doing common things within improved performance. Like you can make splitDate take an array and return an array, instead of multiple method calls.
Finally the gem of all, and it's my favorite, I was using custom data grids to populate tabular data, but since the time I have encountered W2UI Grid, I have fallen in love with it. It can render millions of records at a single time, and it works by rendering only few records to display and as the scroll happens it renders other records and removes the records from DOM that are no longer visible. It's super easy to configure, so you can also use this grid instead of your custom table (if you are not using pagination).
So this is just a brief summary of the immediate measures that you can make to improve the load-time and rendering performance.
One more important tip is that you should also check the size of the response coming from server, if its big and is in mega-bytes, then you will have to optimize your API also, and only provide UI with the things that it needs, don't provide excessive things, as it slows up stuff at the UI.
Applying pagination is best solution, else you need to optimise your code (though the performance is negligible, but help to prevent browser crash if you have more data)
For example :
$.each(dataSet, function (key, value) {
// Append to table body
$table.find('tbody').append($row);
}
should be write as
var $row='"";
$.each(dataSet, function (key, value) {
$row +="something";
}
// Append to table body
$table.find('tbody').append($row);
You are using .append() inside $.each() this mean append() trigger for each loop.
It good if you append it outside loop.
Instead of $table.find('tbody').append($row) you can cache .find() in a variable and then direct append it
I have a hybrid text field & dropdown inputs on my code using selectize. I want that upon change of value the text field will be emptied. How do I do these? Below is my code.
var $select = $('#prod_search').selectize({
persist: false,
maxItems: 1,
valueField: 'stock_no',
searchField: ['name', 'barcode', 'stock_no'],
onChange: function(value) {
search_prod(value);
//insert code that empties the text field//
},
options: products_list,
render: {
item: function(item, escape) {
return '<div>' +
(item.name ? '<span class="name">' + escape(item.name) + '</span>' : '') +
'</div>';
},
option: function(item, escape) {
var label = item.stock_no + ' - ' + item.name || item.stock_no;
var caption = item.name ? item.stock_no : null;
return '<div>' + '<span class="">' + escape(label) + '</span>' + '</div>';
}
}
});
Perhaps use clear(true) (see API reference).
How can I set the URL value in my JSON file to a hyperlink when displayed in the table?
JSON
{
"one": "http://urltovideo.mp4",
"two": "The second list item",
"three": "The third list item"
}
jQuery
$.getJSON( "test.json", function( data ) {
var items = [];
$.each( data, function( key, val ) {
items.push( "<div class='panel panel-default'><div class='panel-heading'>" + key + "</div><div
class='panel-body'><a href=''>" + val + "</a></li></div></div>" );
});
$( "<ol/>", {
"class": "my-new-list",
html: items.join( "" )
}).fadeIn(900).appendTo( "body" );
});
You need to check if val is containing a link:
$.each( data, function( key, val ) {
var link = (val.indexOf('http') > -1) ? '' + val + '' : val;
items.push( '<div class="panel panel-default"><div class="panel-heading">' + key + '</div><div
class="panel-body">' + link + '</li></div></div>');
});
Also, for cleaner code, you should use " for HTML attributes like class="..." and ' for Javascript strings, like: '<a href="' + val + '">'
See jsFiddle