I have a function that updates a comment to every single cell in a row. This function is called many times by a higher level function that loops through every row in the table and determines what comments to apply.
This all works fine. See a simplified version of the code below.
// Loop through all hot rows and determine comment to apply
var loopThroughHotRows = function (hot) {
var rows = hot.getSourceData().length;
for (var i = 0; i < rows; i++) {
var comment = "some comment determined by another function";
applyResponseCommentsToRow(hot, comment, i);
}
}
// Apply comments to a whole row in a passed handsontable
var applyCommentsToRow = function (hot, comment, logicalrow) {
var cols = hot.countCols();
var commentsPlugin = hot.getPlugin('comments');
for (var i = 0; i < cols; i++) {
// render being issued for each comment set.
// need to restrict rendering somehow.
commentsPlugin.setCommentAtCell(logicalrow, i, comment);
}
}
The problem is that each time a comment is applied to a cell. The rendering of the entire handsontable instance is initiated. Causing the web browser to get blocked/chug/become very slow and responsive until all the rendering is complete.
So my question is. Is there some way to prevent Handsontable from rendering each time that a new comment is applied to a cell? Either by temporarily disabling all rendering or adding the comments in a different manner?
I actually ended up figuring out a solution to this on my own. If you set the comment of the cell by calling the hot.getCellMeta() function.
This actually bypasses the re-rendering of the handsontable. See updated function below.
// Apply comments to a whole row in a passed handsontable
var applyCommentsToRow = function (hot, comment, logicalrow) {
var cols = hot.countCols();
for (var i = 0; i < cols; i++) {
// New method of writing comment to cell. Does not cause handsontable re-render.
hot.getCellMeta(logicalrow, i).comment = {'value':responseComments};
}
// Call render once after all comments have been assigned to row!
hot.render();
}
The first thing I can think of to enhance the speed of your function is to not change the comment in cells when it's not necessary. (old comment value = new comment value). To do that, you simply have to compare both String before doing setCommentAtCell :
if(comment.localeCompare(hot.getPlugin('comments').getCommentAtCell(logicalRow,i)) != 0)
commentsPlugin.setCommentAtCell(logicalRow, i, comment);
I used a little example to quickly test this change that you can find in this JSFiddle. (For the sake of 'quick testing', I trigger the change comment for every cell when I copy : whether you use ctrl+C in the table, or you use the action copy in the context menu).
You will see that it will freeze the first time (as every cell will be modified), but the second time there is no freeze at all since the changes are not necessary.
Related
The JqGrid example at http://www.guriddo.net/demo/guriddojs/edit_add_delete/inline_batch/index.html shows how to use 'batch editing'. However when the edit button is clicked, focus scrolls to the last rows initial edit column. How can that be made to focus on the first row instead? Also how can it be used with paging?
The first requirement can be accomplished if you loop in reverse order, since the ids are get from the first row to the last - i.e. the function startEdit can look like:
function startEdit() {
var grid = $("#jqGrid");
var ids = grid.jqGrid('getDataIDs');
for (var i = ids.length - 1; i >= 0; i--) {
grid.jqGrid('editRow',ids[i]);
}
}
As for the other requirements it depends what should be done when the new page is requested - to save existing edits or cancel them.
For this purpose it is needed to use onPaging and maybe onSortCol (in case of sorting) events, but all depend on the specific requirements above
I have a html5 Canvas animation that I am doing on Adobe Animate and tweaking with some code.
I have a portion on the animation that will be like a combobox with all the links to navigate through the different frames. The thing is, I don't want to be creating a bunch of EventListener to many buttons because from experience I know that doesn't work so well. So I am trying to think of a more creative solution. This is my idea.
Create an array that will contain all the buttons.
Assing a variable for each target frame.
Create a for loop with a function inside that assigns the listener to the selected button and then points it to the desired frame (variable)
This is what I have got so far, (not much)
var combobox = [this.btncasco , this.btnbanyera , this.btnLumbrera , this.btnproapopa, this.btnestriborbabor ];
for (var i=0; i<combobox.length; i++) {
var clipcasco = gotoAndStop(0);
var clipbanyera = gotoAndStop(2);
var cliplumbera = gotoAndStop(4);
var clipproapoa = gotoAndStop(6);
var clipestriborbabor = gotoAndStop(8);
}
Would that be feasible ?
In your example, you are just assigning the result of gotoAndStop (with no scope, so likely you're getting an error in the console)
I think you are looking for something like this:
for (var i=0; i<combobox.length; i++) {
// This is kind of complex, but if you just reference "i" in your callback
// It will always be combobox.length, since it references the value of i
// after the for loop completes variable.
// So just store a new reference on your button to make it easy
combobox[i].index = i*2; // x2 lines up with your code 0,2,4,etc.
// Add a listener to the button
combobox[i].on("click", function(event) {
// Use event.target instead of combobox[i] for the same reason as above.
event.target.gotoAndStop(event.target.index);
}
}
You might have the same problem as your other StackOverflow post where the button is undefined (check the console). There is actually a bug in Animate export right now where children of a clip are not immediately available. To get around this, you can call this.gotoAndStop(0); at the start to force it to update the children.
everyone. There's an issue with Chrome (I don't know if it happens with other browsers as well). The situation is with a website that is a few years old. Was developped with standard JavaScript, not jQuery. There are thousands of codelines, so updating it now is not an option.
The thing is, this reads data from a database at the server, which returns some times 10 rows, some times over 500. That is the normal operation. In any case, the request is done via simple AJAX, and it returns an XML. The database responds in less than a second, the formatting of the XML is also in a second, maybe two on a slow day. But when JavaScript receives it back, that's the problem. With 10 elements in the XML there's no problem, but when it's 500, I want to kill myself.
After days of debugging and trying to find the problem, I finally did, but don't know how to fix it. The problem is that when the XML returns, every element is displayed in a simple <table>, meaning every node is basically a <tr> and every node's child is a <td> and <input type="text">. In the end, no matter how many rows it has, the table has 7 columns.
So here's an example of what it does. It simply goes through the nodes and creates the rows and cells with their input fields:
var table = document.getElementById("theTable");
for (var i = 0; i < theXML.childNodes.length; i++)
{
var row = table.insertRow(table.rows.length);
row.style.backgroundColor = "Red";
for (var j = 0; j < theXML.childNodes[i].childNodes.length; j++)
{
var cell = row.insertCell(row.cells.length);
cell.width = "200px";
cell.className = "cellClass";
var d = document.createElement("input")
d.type = "text";
d.width = "190px";
cell.appendChild(d);
}
}
Now, the problem
The code does what it's supposed to do, the table is created with the right rows, columns and inputs, but each and every single time it goes through a width, appendChild, className or other attribute setting, Chrome calls a DOMSubtreeModified event, which is first defined in the Startup() function in the content.js file (not part of my project, so it most be Chrome's). The event calls a onDomChange function which receives document as parameter, which only calls updateDocumentListeners and sends it the parameter, which calls updateInputListeners, which goes through EVERY SINGLE INPUT in the document using a for(), just to call updateInputListeners, which only return true or false depending on some conditions.
Now, when there are only 10 rows, that means that in the end we'll have 70 inputs, which in turn means that by the end it would have called the onDomChange several times, that is: 70 + 69 + 68 + ...., because it goes through everything that's been added before. And that was to be also multiplied by every single attribute you set.
In other words, with every input created, it calls onDomChange. On every attribute I set, it calls onDomChange. If I add a second input, it goes twice, once for the element and/or the attribute I'm setting and another for the previous one. Here's what it does:
//This is the code I found while debugging
function updateDocumentListeners(document)
{
var inputs = document.getElementsByTagName('INPUT');
for (var i = 0; i < inputs.length; ++i)
updateInputListeners(inputs[i]);
}
With 500 inputs, meaning also 500 cells, it simply won't end. Oh, and if there are already other inputs (including types hidden, radio, etc), they are included in the cycle. And then Chrome has the audacity to send a message with an option telling me that it's taking too long and prompts for whether I want to wait or cancel.
What I've tried
Since it all happens because of the event, I've tried to remove it, process the table and then reinstate it, but I can't remove it, because it's declared like this in content.js
document.addEventListener('DOMSubtreeModified', function(){ onDomChangeNoThrow(document); }, true);
This means I can't use removeEventListener, because it is defined as an anonymous function. I've already tried it.
Any suggestions?
Thanks.
Well, here I continue, trying to learn this very nice language... So I previously had a very ugly code full of 'document.write()' and more ugly things, and now I am transforming it into a very nice standards compliant code, and I am liking it! But I found a problem where I don't see the logic. Here it goes:
In the html file I have this:
<body onload="generatetable(0)">
And in the .js file I have this:
function generatetable(product) {
var tbinfo = document.createElement("table"); //table
var tbbody = document.createElement("tbody"); //tbody
var row = document.createElement("tr"); // creates row
for (var i = 0; i < 4; i++) { // creates 4 cells
var cell = document.createElement("td");
}
var tname = arraypro[product].Name;
cell.appendChild(tname);
(I don't paste the rest of the table, because it seems to work fine)
And when running, I get the 'Exception 8' error on the var tname = arraypro[product].Name line
But if I do just an
alert(arraypro[0].Name);
it outputs exactly what it should output, the very right word. How it's possible that the value in arraypro[product].Name can be retrieved by an alert (if you pass the value of 'product') but not by the appendChild?
I am still not very well used to the logic of programming, but I try...
PD: The arraypro , where the information is, has been declared as a global value, accessible for everything, in case it matters to know.
Thanks very much for any input here.
appendChild() expects a DOM-Node, you're passing in a string I believe.
You can change it to:
var tname = arraypro[product].Name;
cell.appendChild(document.createTextNode(tname));
How do I remove an items from a data bound array? My code follows.
for(var i = 0; i < listBox.selectedIndices.length; i++) {
var toRemove = listFiles.selectedIndices[i];
dataArray.splice(toRemove, 1);
}
Thanks in advance!
Edit Here is my swf. The Add Photos works except when you remove items.
http://www.3rdshooter.com/Content/Flash/PhotoUploader.html
Add 3 photos different.
Remove 2nd photo.
Add a different photo.
SWF adds the 2nd photo to the end.
Any ideas on why it would be doing this?
Edit 2 Here is my code
private function OnSelectFileRefList(e:Event):void
{
Alert.show('addstart:' + arrayQueue.length);
for each (var f:FileReference in fileRefList.fileList)
{
var lid:ListItemData = new ListItemData();
lid.fileRef = f;
arrayQueue[arrayQueue.length]=lid;
}
Alert.show('addcomplete:' + arrayQueue.length);
listFiles.executeBindings();
Alert.show(ListItemData(arrayQueue[arrayQueue.length-1]).fileRef.name);
PushStatus('Added ' + fileRefList.fileList.length.toString() + ' photo(s) to queue!');
fileRefList.fileList.length = 0;
buttonUpload.enabled = (arrayQueue.length > 0);
}
private function OnButtonRemoveClicked(e:Event):void
{
for(var i:Number = 0; i < listFiles.selectedIndices.length; i++) {
var toRemove:Number = listFiles.selectedIndices[i];
//Alert.show(toRemove.toString());
arrayQueue.splice(toRemove, 1);
}
listFiles.executeBindings();
Alert.show('removecomplete:' + arrayQueue.length);
PushStatus('Removed photos from queue.');
buttonRemove.enabled = (listFiles.selectedItems.length > 0);
buttonUpload.enabled = (arrayQueue.length > 0);
}
It would definitely be helpful to know two things:
Which version of ActionScript are you targeting?
Judging from the behavior of your application, the error isn't occurring when the user removes an item from the list of files to upload. Looks more like an issue with your logic when a user adds a new item to the list. Any chance you could post that code as well?
UPDATE:
Instead of: arrayQueue[arrayQueue.length]=lid
Try: arrayQueue.push(lid)
That will add a new item to the end of the array and push the item in to that spot.
UPDATE 2:
Ok, did a little more digging. Turns out that the fileList doesn't get cleared every time the dialog is opened (if you're not creating a new instance of the FileReferenceList each time the user selects new files). You need to call splice() on the fileList after you add each file to your Array.
Try something like this in your AddFile() method...
for(var j:int=0; j < fileRefList.fileList.length; j++)
{
arrayQueue.push(fileRefList.fileList[j]);
fileRefList.fileList.splice(j, 1);
}
That will keep the fileList up to date rather than holding on to previous selections.
I see one issue. The selected indices are no longer valid once you have spliced out the first element from the array. But that should only be a problem when removing multiple items at once.
I think we need to see more code about how you are handling the upload before we can figure out what is going on. It looks to me like you are holding a reference to the removed FileReference or something. The described problem is occurring when you upload a new file, not when you remove the selected one.
Do you mean to use listBox and listFiles to refer to the same thing?
I'm stepping out on a limb here, because I don't have a ton of experience with JavaScript, but I'd do this the same way that I'd do it in C, C++, or Java: By copying the remaining array elements down into their new locations.
Assuming that listFiles.selectedIndices is sorted (and its contents are valid indices for dataArray), the code would be something like the following:
(WARNING: untested code follows.)
// Don't bother copying any elements below the first selected element.
var writeIndex = listFiles.selectedIndices[0];
var readIndex = listFiles.selectedIndices[0] + 1;
var selectionIndex = 1;
while(writeIndex < (dataArray.length - listFiles.selectedIndices.length)) {
if (selectionIndex < listFiles.selectedIndices.length) {
// If the read pointer is currently at a selected element,
// then bump it up until it's past selected range.
while(selectionIndex < listFiles.selectedIndices.length &&
readIndex == listFiles.selectedIndices[selectionIndex]) {
selectionIndex++;
readIndex++;
}
}
dataArray[writeIndex++] = dataArray[readIndex++];
}
// Remove the tail of the dataArray
if (writeIndex < dataArray.length) {
dataArray.splice(writeIndex, dataArray.length - writeIndex);
}
EDIT 2009/04/04: Your Remove algorithm still suffers from the flaw that as you remove items in listFiles.selectedIndices, you break the correspondence between the indices in arrayQueue and those in listFiles.selectedIndices.
To see this, try adding 3 files, then doing "Select All" and then hit Remove. It will start by removing the 1st file in the list (index 0). Now what had been the 2nd and 3rd files in the list are at indices 0 and 1. The next value taken from listFiles.selectedIndices is 1 -- but now, what had been the 3rd file is at index 1. So the former File #3 gets spliced out of the array, leaving the former 2nd file un-removed and at index 0. (Using more files, you'll see that this implementation only removes every other file in the array.)
This is why my JavaScript code (above) uses a readIndex and a writeIndex to copy the entries in the array, skipping the readIndex over the indices that are to be deleted. This algorithm avoids the problem of losing correspondence between the array indices. (It does need to be coded carefully to guard against various edge conditions.) I tried some JavaScript code similar to what I wrote above; it worked for me.
I suspect that the problem in your original test case (removing the 2nd file, then adding another) is analogous. Since you've only shown part of your code, I can't tell whether the array indices and the data in listFiles.selectedIndices, arrayQueue, and fileRefList.fileList are always going to match up appropriately. (But I suspect that the problem is that they don't.)
BTW, even if you fix the problem with using splice() by adjusting the array index values appropriately, it's still an O(N2) algorithm in the general case. The array copy algorithm is O(N).
I'd really need to see the whole class to provide a difinitive answer, but I would write a method to handle removing multiple objects from the dataProvider and perhaps assigning a new array as the dataProvider for the list instead of toying with binding and using the same list for the duration. Like I said, this is probably inefficient, and would require a look at the context of the question, but that is what I would do 9unless you have a big need for binding in this circumstance)
/**
* Returns a new Array with the selected objects removed
*/
private function removeSelected(selectedItems:Array):Array
{
var returnArray:Array = []
for each(var object:Object in this.arrayQueue)
{
if( selectedItems.indexOf(object)==-1 )
returnArray.push( object )
}
return returnArray;
}
You might be interested in this blog entry about the fact that robust iterators are missing in the Java language.
The programming language, you mentioned Javascript, is not the issue, it's the concept of robust iterators that I wanted to point out (the paper actually is about C++ as the programming language).
The [research document]() about providing robust iterators for the ET++ C++ framework may still e helpful in solving your problem. I am sure the document can provide you with the necessary ideas how to approach your problem.