I have a big table, here is a section of it:
My goal is to make each section collapsible and uncollapsible by toggling all section rows apart from the Sum row and moving the section heading to the empty sum row cell, then moving it back to its original form when it is next toggled.
I have tried to approach this from a few angles but as I'm a jQuery novice, I can't for the life of me find an efficient way to do it.
The first part of the problem is actually defining the sections. As they're of varying lengths long, I figured I'd first need to cycle through each row and check if the first cell is empty or not. If it isn't, I assume it's a new section, if it is, I assume it's another and add it to the current section.
Then for each section's first row and cell I set an on click event that toggles all rows apart from the Sum row and moves the section title into it.
I have managed to accomplish this but it uses ungodly amounts of fugly Javascript and is very slow. So like I said, I'm wondering how I can do this in the most efficient way possible.
In essence, is there a nice way I can select all sections using just jQuery selectors instead of having to loop through every single row and inspect each cell etc? I'm happy just to know if there's a more efficient way to do it or not, but a solution would be much appreciated.
For the sake of curiosity, here is the code I used to colour the image:
var rowCount = $("tr").length;
var rows = $("tr > td:first-child").not("tr:nth-child(1) > td:first-child").not("tr:nth-child("+rowCount+") > td:first-child").not("tr:nth-child("+(rowCount-1)+") > td:first-child");
var sectionTitles = $(rows).filter(function(){
return !($(this).text()=="");
});
var sectionItemTitles = $(rows).not(sectionTitles);
sectionTitles.css({"background":"red"});
sectionTitles.attr("type","section");
sectionItemTitles.css({"background":"blue"});
sectionItemTitles.attr("type","row");
To achieve this functionality you will have to identify the rows which need to be grouped togetherand add a class and an image of plusminus to indicate collapsible space. On the click function of the plusminus, use the toggle() function to collapse or uncollapse the grouped data.
toggle jquery
Related
So I've been working on this web-project that demands a gallery with a slider underneath it. I've used this JavaScript so far to solve the problem in a forEach(element) function:
var divnumber = Array.from(element.parentNode.children).indexOf(element);
So the pagination changes by the index of the clicked element.
But since I need to make it responsive and the graphic designer demands something different in the mobile view I would need to get the number of the divs by using their class. Basically - the same array but different values.
Is there any way to tweak that line of code a bit to let it get the index of the element by its class instead of their parent? Here's the pen for more: https://codepen.io/ridonibishi/pen/BaNyBva
Thank you in advance!
Try using:
var divnumber = Array.from(document.getElementsByClassName('class')).indexOf(element);
It works as intended.
I'd like to use Javascript to set up the display logic for individual rows in a single-choice matrix table question in Qualtrics. I've always done this in the past with the usual click-through method, but I often have 100+ rows to do this for, and it'd save a ton of time to be able to do this programmatically.
I've tried inserting the following into "Add JavaScript" for the question I'm trying to add the display logic to:
Qualtrics.SurveyEngine.addOnload(function()
{
/*Set display logic*/
if ('${q://QID2/SelectedAnswerRecode/1}' < 3) {'${q://QID3/ChoiceDescription/1}'.style.visibility='hidden';}
if ('${q://QID2/SelectedAnswerRecode/2}' < 3) {'${q://QID3/ChoiceDescription/2}'.style.visibility='hidden';}
});
The idea is that an answer with a value of at least 3 in row 1 of QID2 (also a single-choice matrix table) is required for row 1 of matrix table QID3 to appear, and so on. As it is, is appears unresponsive - rows in QID3 are still displayed even if selected values in the corresponding rows of QID2 are < 3.
I've also tried style.display='none' instead of style.visibility='hidden' with no success. My experience with Javascript is limited, so I suspect it's some kind of syntax issue.
The problem is indeed with your syntax. You need to hide an html element and '${q://QID3/ChoiceDescription/1}' is not an html element (it is the innerHTML of a label). Even if it were an html element, the syntax is wrong (it wouldn't be in quotes).
It is best to use prototypejs when possible, so if the element were named 'element' the command would be:
element.hide();
To find the correct elements to hide, you need to identify and find them by element id or some combination of element tag, class and attribute. It could be done using choice description, but it would take a lot more code and wouldn't be very efficient.
Use Inspect Element in the browser with the survey in Preview mode to find the element ID for the row header of the matrix where the display logic will occur. .up().hide() will grab the rest of the row as well. Quotes around the IDs are necessary because of the '~' in the ID names; otherwise I get an "unexpected token ~" error when trying to save it. Thanks to T. Gibbons for pointing me in the right direction.
Qualtrics.SurveyEngine.addOnload(function()
{
/*Set display logic*/
if ('${q://QID2/SelectedAnswerRecode/1}' < 3) {$('header~QID3~1').up().hide();}
if ('${q://QID2/SelectedAnswerRecode/2}' < 3) {$('header~QID3~2').up().hide();}
if ('${q://QID2/SelectedAnswerRecode/3}' < 3) {$('header~QID3~3').up().hide();}
if ('${q://QID2/SelectedAnswerRecode/4}' < 3) {$('header~QID3~4').up().hide();}
if ('${q://QID2/SelectedAnswerRecode/5}' < 3) {$('header~QID3~5').up().hide();}
});
Copy/paste/edit as needed.
UPDATE
Looks like things may have changed with updates, but while this works with the question itself, this can mess with the logic on subsequent questions. Use extreme caution!
I have a table of dynamically generated content (PHP from mysql). The first column is always a date. Some records share the same date.
My goal is to style all records with the same date with the same background color and for the date to only appear in the first event of that day.
I'm no javascript/jquery expert, but here is my first go. It is not yet fully functional.
Here are my two problems:
1: Cannot get the repeated dates to disappear (see note in code)
2: The whole method of adding classes according to the content of the cell above seems incredibly slow. Page loads are around 10-12 seconds with a table of about 100-150 rows. Is this the fastest method to achieve this?
$(document).ready(function(){
$("td.date").each(function(){
var cellAbove = $(this).parent().prev().children(".date").html();
if ($(this).html()!==cellAbove){$(this).parent().addClass("firstDate");}
if ($(this).html()==cellAbove){$(this).parent().addClass("multiDate");}
});
$("tr.firstDate").filter(":even").css("background-color","#FFFFFF");
$("tr.firstDate").filter(":odd").css("background-color","#F4F4F4");
$("tr.multiDate").each(function(){
$(this).children(".date").text(); //NOT FUNCTIONING
$(this).css("background-color",$(this).prev().css("background-color"));
});
});
<table>
<tr>
<td>DATE</td>
<td>EVENT</td>
</tr>
<tr>
<td class="date">January 5, 2013</td>
<td>Board Meeting</td>
</tr>
...
</table>
I think the fastest implementation would be to filter out the values and set your html classes server-side using PHP.
But I went ahead and wrote a JS version for you anyways (and for my morning problem-solving) =)
Your first function was at almost a 2n algorithm because you were running 2 different loops. My first suggestion is to do the work in the first loop. Also, every time you use a jQuery selector, you are also performing a loop. jQuery isn't instantaneous, and is essentially traversing all DOM elements (another loop!) to find your elements. I count 7 jQuery selectors in your code, and 2 "each" loops, for a total of 9 loops. You can save your jQuery objects if they will be reused, so you don't have to "re-loop" to acquire them. For example, in the future you can save your $("tr.firstDate") object in a variable since you use it twice. In my code, you can see that I save my $me variable for future use. If I wanted to make it even faster, I could save the $me.html() value as well. Using this approach for much larger applications, you would have to consider the trade-off of memory size vs. speed.
Also, I used the .html("") method to clear the contents of the cells you wanted empty.
Another suggestion is to use CSS to decide the color, rather than setting the color using jQuery. Add the class and then have your CSS do the work. You can see in my code that I only add the .alt class to the rows I want with the alternate color.
.alt td { background: #666; }
Also, don't rely on styling the tr background color. I don't think this is supported cross-browser. I would style the td instead. Also, in my jsFiddle I used th tags for your headers, for semantic purposes.
Here is a jsFiddle. The javascript is below:
$(document).ready(function(){
var parity = true;
var curDate = null;
$("td.date").each(function(){
var $me = $(this);
if(curDate == null || curDate != $me.html())
{
curDate = $me.html();
parity = !parity;
}
else
$me.html("");
if(parity)
$me.parent().addClass("alt");
});
});
Hope this helps!
I am using JQuery to manipulate a page which I do not have access to modify server-side. One of the main things I need to do is create a copy of just a sub-section of one of the tables on the page. More specifically, I need to create 2 tables, one which is a copy of just the first two rows of the table and another which is just the first column.
After optimizing it as far as I could, I'm fairly happy with the performance of the row copy but the column copy is still a bit on the slow side.
I have the following, where sourceTable is the original table and titleColBody is the tbody of a new table created earlier:
sourceTable.find("tr").each(function(){
titleColBody.append(
$("<tr></tr>").append($(this).children(":first").clone())
);
});
On a table with roughly 50 rows and a huge number of columns, this takes approx. 350ms in IE8. Not terrible, but I would like to bring this down a little if possible.
My question is if there is a more efficient way of doing this in JQuery.
Similarly, I create the copy of the first 2 rows of the table as below.
headerTable.find("tbody")
.append(sourceTable.find("tr:eq(0)").clone())
.append(sourceTable.find("tr:eq(1)").clone());
This is fairly quick, however if there is a better way of doing it I would be happy to hear it.
Any optimization at all would be greatly appreciated.
Seems like this might be a tad bit faster for the columns, this way you'll only grab one resource set instead of grabbing two sets (one for the TRs and then one for the child TDs):
sourceTable.find("tr > td:first-child").each(function(){
titleColBody.append($("<tr></tr>").append($(this).clone()));
});
Also on another note, if the "titleColBody" is a pointer to a DOM object that causes the browser to have to render the page when it's updated, you're better off appending to a documentFragment and then appending that fragment (so the browser only re-renders once).
For instance:
var titleCol = document.createDocumentFragment();
sourceTable.find("tr > td:first-child").each(function(){
titleCol.append($("<tr></tr>").append($(this).clone()));
});
titleColBody.append(titleCol);
I am creating a large table dynamically using Javascript.
I have realised the time taken to add a new row grows exponentially as the number of rows increase.
I suspect the Page is getting refreshed in each loop per row (I am also adding input elements of type text on each cell)
Is there a way to stop the page "refreshing" until I am done with the whole table?
Any suggestion on how to do this or go around it?
I have tried all the suggestions above, but I am still getting the performance bottlenecks.
I tried to analyse line after line, and I have noted that document.getElementById() is one of the lines taking a lot of time to execute when the table is very large.
I am using getElementById() to dynamically access an HTML input of type text loaded on each cell of the table.
Any ideas on which DOM method I should use instead of getElementById()?
You can create the table object without adding it to the document tree, add all the rows and then append the table object to the document tree.
var theTable = document.createElement("table");
// ...
// add all the rows to theTable
// ...
document.body.appendChild(theTable);
Maybe build your table as a big string of HTML, and then set the .innerHTML of a container div to that string when you've finished?
Did you try the <tbody> tag when you create the table?
It is possible browsers optimize that and don't "refresh" the table while populating it.
If your cells sizes are the same for each row then you will be able to specify style table-layout:fixed - this will give you the greatest performance gain as the browser won't have to recalculate cells sizes each time a row is added
Use the table DOM to loop trough the rows and cells to populate them, instead of using document.getElementByID() to get each individual cell.
E.g.
thisTable = document.getElementByID('mytable');//get the table
oneRow = thisTable.rows[0].cells; //for instance the first row
for (var colCount = 0; colCount <totalCols; colCount ++)
{
oneCell =oneRow[colCount];
oneCell.childNodes[0].value = 'test';//if your cell contains an input element
oneCell.innerHTML = 'test'; // simply write on the cell directly
}
Hope that helps someone else...
Cheers.