I have a page with a list of items. Items have several actions assigned to them. (see screenshot).
One may choose to directly click on an icon next to each row or check a checkbox on the left hand side.
The issue is that after clicking an item OR checking a checkbox of several items and then clicking an action there is a lag (a second or so). Imagine having 100 rows or more.
How can I improve the performance of my javascript code?
sample HTML of one row:
<tr id="1960AGIMMGMRTB20314" class="">
<td class="checkbox">
<input type="checkbox" value="1960" class="checkbox">
</td>
<td class="">
<p>GD009000246</p>
</td>
<td class="platform">PCGames</td>
<td class="cat">Up</td>
<td class="platform">
<div class="pbar"><span class="progresslabel"></span></div>
</td>
<td class="date">10.48.1.236</td>
<td class="options clearfix">
<a title="" class="iconMagnifier tip" href="/Packages/View/AGI-MM-GM-RTB-2.0.3.1.4">View</a>
<a title="" href="/Packages/PackageActionAsyncDeletePackage" data-ajax-type="DeletePackage" data-ajax-packageid="AGI-MM-GM-RTB-2.0.3.1.4" data-ajax-machineid="1960" class="iconDelete action tip">Remove</a>
</td>
</tr>
javascript:
// action invoker
$("a.action:not(.remove)").click(function (e) { // .remove => do not execute on download tasks page
var obj = $(this);
e.preventDefault();
if (!$(this).hasClass('disablelink')) {
var machineIds = getSelection(obj);
if (machineIds.length > 0) {
packageAction(obj.attr("data-ajax-packageid"), machineIds, obj.attr("data-ajax-type"));
};
}
$(".checkall").attr("checked", false);
});
function getSelection(obj) {
var selected = new Array();
if (obj.attr('data-ajax-machineId')) {
selected.push(obj.attr('data-ajax-machineId'));
} else {
$("input.checkbox:checkbox:checked:not(.checkall)").each(function () {
var machineId = $(this).val();
var packageId = obj.attr("data-ajax-packageid");
var operation = obj.attr("data-ajax-type");
if ($("#" + machineId + packageId.removeSpecialChars().toUpperCase() + "").size() != 0) {
var row = $("#" + machineId + packageId.removeSpecialChars().toUpperCase() + "");
row.has("a[data-ajax-type=" + operation + "]:not(.hide)").length ? selected.push(machineId) : $(this).attr('checked', false);
}
});
}
return selected;
}
// download, install, uninstall, remove, activate, deactivate package
function packageAction(packageId, machineIds, operationType) {
.....// to implement - not needed
Querying objects out of the DOM is slow. The best thing to do is hold all of your data in javascript objects, do all the calculations and stuff you want, THEN update the DOM all at once. Ember.js and some other javascript libraries/tools have bound data which is cool, meaning you change an attribute in the javascript object and it automatically updates the DOM!
Related
The code below has a select with three options. When you choose one of the options and click add the option is added to the table. If the user were try to choose the same option I need an alert or modal window to appear saying "Duplicates not allowed."
Anyone have an idea how to accomplish that?
$("select#keys").change(function(){
$("#add-user-code").click(function(){
var selectedKey = $("#keys").val();
$("#3rd-row").show();
$('#example').html('<span class="lbl">' + selectedKey + ' </span>');
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<html>
<body>
<select class="select-duc" id="keys">
<option></option>
<option>Allergies</option>
<option>Animals</option>
<option>Coughing</option>
</select>
<button type="button" id="add-user-code" class="btn btn-default pull-right" data-dismiss="modal">Add User Code</button>
<div class="col-sm-6 reset">
<div class="details-page-container two">
<h5>User Codes</h5>
<div class="table-container">
<table>
<tbody><tr>
<th><strong>Code</strong></th>
<th><strong>Description</strong></th>
<th><strong>Domain</strong></th>
<th><strong>Start Date</strong></th>
<th><strong>End Date</strong></th>
<th><strong>Delete</strong></th>
</tr>
<tr>
<td>01</td>
<td>MINNEAPOLIS</td>
<td>MN</td>
<td>11/01/2019</td>
<td></td>
<td>Delete</td>
</tr>
<tr>
<td>02</td>
<td>MINNEAPOLIS</td>
<td>MN</td>
<td>11/01/2019</td>
<td></td>
<td>Delete</td>
</tr>
<tr id="3rd-row" class="hideIT">
<td id="example"></td>
<td>MINNEAPOLIS</td>
<td>MN</td>
<td>11/01/2019</td>
<td>12/01/2019</td>
<td><a data-toggle="modal" data-target="#myModal2" href="#">Delete</a></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</body>
</html>
Define an array, and
In your $("#add-user-code").click function:
Check if the value(which user selected) is already present in the array or not(To show the alert),
push the index/value to the array after adding it to your table(So that next time the condition fails and alert would show up).
Also, don't forget to remove items from the array whenever needed(User Starts again or item is removed from the table)/
I'd better just hide the duplicate option when the one is selected and show them back when/if its unselected, for better user experience.
Otherwise, you can make an array of selected values and loop through it when an option selected, something like this:
var selectedOptions = [];
$("select#keys").change(function(){
$("#add-user-code").click(function(){
var selectedKey = $("#keys").val();
if($.inArray(selectedKey, selectedOptions)) {
alert ("Duplicate values are not allowed");
} else {
selectedOptions.push(selectedKey);
}
$("#3rd-row").show();
$('#example').html('<span class="lbl">' + selectedKey + ' </span>');
});
});
Still, it has to be reset when options are unselected, I don't see multiple options in your snippet, though
You can create a function to return all the values to check whether the value the user wants to insert does not exits already
function getTableValues() {
return $('tr').find('td:first-child').map(function() {
return $( this ).text();
}).get();
}
$("select#keys").change(function(){
$("#add-user-code").click(function(){
var selectedKey = $("#keys").val();
if(!getTableValues().includes(selectedKey)) {
$("#3rd-row").show();
$('#example').html('<span class="lbl">' + selectedKey + ' </span>');
}
else {
alert('Duplicate not allowed');
}
});
I don't think you need the .click() bind inside of the .change() bind. Also, rather than using an alert, you could just make that option not available.
$("#add-user-code").click(function(){
$('#example').html('<span class="lbl">' + $('#keys option:selected').hide().text() + ' </span>');
$("#3rd-row").show();
$('#keys option:eq(0)').prop('selected', 'selected'); //set the select back to the blank option
});
Then if you click delete:
$('a').click(function() {
let example = $('#example .lbl').text().trim();
$('#keys option').filter(function() {return this.textContent === example; }).show();
$("#3rd-row").hide();
});
You shouldn't need an array or function to achieve what you're after.
https://jsfiddle.net/g9523ysz/
If you really need an alert:
$("#add-user-code").click(function(){
let option = $('#keys option:selected').text();
if (option) {
if ($('td').filter(function() { return $(this).find('.lbl').text().trim() === option; }).length){
alert('Duplicates not allowed.');
} else {
$("#3rd-row").show();
$('#example').html('<span class="lbl">' + option + ' </span>');
}
}
});
I would suggest that if you can, use a more specific class for the span, like lbl-user-code. Then rather than having to check every single td you could:
if ($('.lbl-user-code').filter(function() { return this.textContent === option; }).length) { ... }
https://jsfiddle.net/9us4d08j/
I have a simple table with a Select button for each row that when clicked calls a PHP script to update a Session Variable with the ID of the selected Item. Here's the table:
<tr class="" id="PR9215">
<td>CODE A</td>
<td>Fresh Frust</td>
<td class="text-center"><button type="button" class="btn btn-success btn-sm">Select</button></td>
</tr>
<tr class="" id="PR9594">
<td>Oranges</td>
<td>Fresh Oranges</td>
<td class="text-center"><button type="button" class="btn btn-success btn-sm">Select</button></td>
</tr>
<tr class="" id="PR9588">
<td>MANGO</td>
<td>Fresh Mango</td>
<td class="text-center"><button type="button" class="btn btn-success btn-sm">Select</button></td>
</tr>
and here's the script that it calls:
$(document).ready(function() {
$('button.btn-success').click(function() {
var itemID = $(this).closest('tr').attr('id');
// Create a reference to $(this) here:
$this = $(this);
$.post('updateSelections.php', {
itemID: itemID,
selectionType: 'yes'
}, function(data) {
data = JSON.parse(data);
if (data.error) {
var ajaxError = (data.text);
var errorAlert = 'There was an error updating your selections - ' + ajaxError + '. Please contact Support';
$this.closest('tr').addClass("warning");
$('#alert_ajax_error').html(errorAlert);
$("#alert_ajax_error").show();
return; // stop executing this function any further
} else {
console.log('update successful - success add class to table row');
$this.closest('tr').addClass("success");
$this.closest('tr').removeClass("danger");
//$(this).closest('tr').attr('class','success');
}
}).fail(function(xhr) {
var httpStatus = (xhr.status);
var ajaxError = 'There was an error updating your selections - AJAX request error. HTTP Status: ' + httpStatus + '. Please contact Support';
console.log('ajaxError: ' + ajaxError);
$this.closest('tr').addClass("warning");
$('#alert_ajax_error').html(ajaxError);
$("#alert_ajax_error").show();
});
});
});
This is working when it comes to making the initial selection - the table row is coloured green to indicate it has been selected. I now need to extend this so that when the Select button is clicked a 2nd time it then removes the green table row highlighting and returns it to it's original state.
Now sure how to go about extending the script to achieve this.
Check below logic for that:
$('button.btn-success').click(function() {
if ($this.closest('tr').hasClass("first_click")) {
$this.closest('tr').removeClass();
//$( "tr" ).removeClass();
return false;
}else{
$this.closest('tr').addClass("first_click");
}
You chould achieve that by using a boolean to track the state of the button. Then check the state of the button before taking action.
Ps. You can chain your addClass() and removeClass() methods.
var buttonSelected = false;
if(buttonSelected){
$this.closest('tr').addClass("success").removeClass("danger");
buttonSelected = true;
} else {
$this.closest('tr').removeClass("success").addClass("danger");
buttonSelected = false;
}
My index.html is here:
<tr class="activity_row" t-att-data-activity_id="activity.id">
<td>
<div>
<t t-if="duration != undefine">
<span class="pt_duration_line"><t t-esc="duration.split(':')[0] + 'h ' + (duration.split(':')[1] and duration.split(':')[1] + 'min' or '')"></t></span> <!-- call format_hour method of this screen -->
</t>
</div>
</td>
</tr>
For first tr tag the onclick action is:
this.$el.find(".activity_row").on("click", this.on_row_click);
on_row_click: function(event) {
var activity_id = $(event.currentTarget).data("activity_id");
if(activity_id) {
var activity = this.project_timesheet_db.get_activity_by_id(activity_id);
this.project_timesheet_widget.screen_selector.set_current_screen("add_activity", activity);
}
},
Here i am getting activity_id successfully. But Inside this row there is internal div which includes span as mentioned on top containing pt_duration_line class. And for this, onclick action is :
this.$el.find(".pt_duration_line").on("click", this.on_url_click);
on_url_click: function(event) {
var act_id = $(event.currentTarget).data("act_id");
if(act_id) {
var activity = this.project_timesheet_db.get_activity_by_id(act_id);
for this internal div I am not getting value of 'act_id' which I am getting for 'activity_id' for parent element row.
In brief: What to do if i want to get activity_id for the div resided in that row.
Thanks in advance
try this:
<tr class="activity_row" t-att-data-activity_id="activity.id">
<td>
<div>
<t t-if="duration != undefine">
<span class="pt_duration_line" t-att-data-act-id="activity.id"><t t-esc="duration.split(':')[0] + 'h ' + (duration.split(':')[1] and duration.split(':')[1] + 'min' or '')"></t></span> <!-- call format_hour method of this screen -->
</t>
</div>
</td>
</tr>
also, as other users mentioned, e and event are not the same, change to this:
this.$el.find(".pt_duration_line").on("click", this.on_url_click);
on_url_click: function(event) {
var act_id = $(event.currentTarget).data("act_id");
if(act_id) {
var activity = this.project_timesheet_db.get_activity_by_id(act_id);
Bit of a strange problem, but I'm not sure how to fix it.
While inside of the code below, I'm trying to take all of the element of a table a sort them based on the row heading that was clicked.
The starting HTML:
<table id="PracticeTable" class="col-md-10 col-md-offset-2">
<tr id="Column Names">
<td class="ClientID col-xs-1">Client ID</td>
<td class="ClientName col-xs-1">Client Name</td>
<td class="PracticeID col-xs-1">Practice</td>
</tr>
#for (int i = 0; i < ViewBag.ClientDatabase.getDatabase().Count; i++)
{
#:<tr class="DataRow">
<td class="ClientID col-xs-1" data-name="#ViewBag.ClientDatabase.getByIndex(i).Id">#ViewBag.ClientDatabase.getByIndex(i).Id</td>
<td class="ClientName col-md-2 col-xs-1" data-name="#ViewBag.ClientDatabase.getByIndex(i).getClientName">#ViewBag.ClientDatabase.getByIndex(i).getClientName</td>
<td class="PracticeIDs col-xs-1" data-name="#ViewBag.ClientDatabase.getByIndex(i).getPracticeID">#ViewBag.ClientDatabase.getByIndex(i).getPracticeID</td>
<td class="EditButtons col-xs-1" data-bind="visible: editVisible">Edit</td>
#:</tr>
}
Example of one of the click functions:
jQuery(document).ready(function () {
$(".ClientName").click(function () {
alert("Name Clicked");
var clients = $(".DataRow");
var compareLine = $(clients).children(".ClientName");
compareLine.sort(function (a, b) {
var an = $(a).attr("data-name");
var bn = $(b).attr("data-name");
alert(an + " " + bn);
if (an > bn) {
return 1;
}
if (an < bn) {
return -1;
}
return 0;
});
compareLine.detach().appendTo(clients);
});
});
When I attempt to append the lines as above, the names from Client Name appear in every row. Is there something I'm missing? I tried to loop through the data in compareLine to add them individually, but I couldn't figure it out.
EDIT: Removing duplicated code from a fix attempt that wasn't fully removed.
If you want to sort the rows, I think you just need to make a small change:
jQuery(document).ready(function () {
$(".ClientName").click(function () {
alert("Name Clicked");
var clients = $(".DataRow");
clients.sort(function(a, b) {
var an = $(a).children(".ClientName").attr("data-name");
var bn = $(b).children(".ClientName").attr("data-name");
alert(an + " " + bn);
if (an > bn) {
return 1;
}
if (an < bn) {
return -1;
}
return 0;
});
clients.detach().appendTo("#PracticeTable");
});
});
Seems like your sort function was designed to be working with the rows, but you were sorting a collection of the columns.
I've a long table made of rows like this
<tr id="row_369696" class="lvtColData" bgcolor="white" onmouseout="this.className='lvtColData'" onmouseover="this.className='lvtColDataHover'">
<td width="2%"></td>
<td bgcolor="#FFFFFF">
27-10-2014
<span style="display:none;" module="Accounts" fieldname="cf_1390" recordid="369696" type="metainfo"></span>
</td>
<td bgcolor="#FFFFFF">
12:30
<span style="display:none;" module="Accounts" fieldname="cf_1380" recordid="369696" type="metainfo"></span>
</td>
</tr>
the end result that i need is to change the background of this row when the time and the date match the content of the columns marked by the fieldname cf_1390 for the date part and the cf_1380 for the time part.
i was thinking of using jquery to cycle trough rows, find the content of the cell, compare it to now date, and if it matches change the row background, but i cannot figure out how to do it.
can someone help me with some jsfiddle example ? :)
Here is an example of looping through your rows, checking if the date & time match the variables (Which i've just hard coded at the top for this example) - and then setting them to red if it finds both a date & a time match in that row.
JS:
var date = "27-10-2014";
var time = "12:32";
$(document).ready(function(){
$('#targetTable tr').each(function(i,e){
var match = 0;
$(this).children('td').each(function(i2,e2){
content = $(e2).html().substring(0, $(e2).html().indexOf('<span')).trim();
if(content == date){ match++; }
if(content == time){ match++; }
});
if(match == 2){
$(this).css('background','red');
$(this).children('td').css('background','red');
}
});
});
Fiddle
Here is an easy-to-understand example.
JSFiddle http://jsfiddle.net/h3Xd3/
HTML
<table id="myTable">
<tr id="row_369696" >
<td bgcolor="#FFFFFF">
27-10-2014
<span style="display:none;" module="Accounts" fieldname="cf_1390" recordid="369696" type="metainfo"></span>
</td>
<td bgcolor="#FFFFFF">
12:30
<span style="display:none;" module="Accounts" fieldname="cf_1380" recordid="369696" type="metainfo"></span>
</td>
</tr>
</table>
CSS .highlight{background-color:lightgrey;}
JQUery
function datesEqual(a, b)
{
return (!(a>b || b>a))
}
$(function () {
// Handler for .ready() called.
$("#myTable tr").each(function(){
//Get date and hour. Split each item by the appropriate separator
var date_row = $(this).find("td:eq(0)").text().split("-");
var hour_row = $(this).find("td:eq(1)").text().split(":");
var date_object = new Date(date_row[2], date_row[1] - 1, date_row[0], hour_row[0], hour_row[1]);
var YOUR_OTHER_DATE = new Date(date_row[2], date_row[1] - 1, date_row[0], hour_row[0], hour_row[1]); //You have to change this line
if ( datesEqual(YOUR_OTHER_DATE, date_object) == true){
$(this).find("td").addClass("highlight");
}
});
});
Don't forget to change the YOUR_OTHER_DATE value. It depends to your need but we don't have enough details to give a complete answer.