Pages: 1
Print
Author Topic: node list issues/questions  (Read 7623 times)
russurquhart1
Member

Posts: 46


« on: May 05, 2009, 01:17:48 PM »

Hi,

As per my question the other day .re xpath, i wrote the following script to change all the filter attributes having the value 'filter5' to null:

     var nodelist = ActiveDocument.getNodesByXpath("//*[@filter='filter5']");   
             
       Application.Alert("Suspected elements to process: " + nodelist.Length);
       
       var current = 0;
       var number_corrected = 0;
       while (current < nodelist.Length)
       {
         var current_node = nodelist.item(current);
               current_node.setAttribute("filter", "");
               number_corrected = number_corrected + 1;
               current = current + 1;
       }
       Application.Alert( (number_corrected  - 1) + " filter attributes corrected");

My first alert, for example, says that there are a total of 26 suspected elements to process. The first pass through, only about 13 were processed.

(I am assuming that, as i specified all elements, and that some of these elements may be contained in others, then only certain elements (siblings) are getting processed at a time. If this is the case, how can i change the loop to get them all in one swoop?

Thanks!


Russ
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #1 on: May 05, 2009, 03:53:54 PM »

One thing I don't think our docs state clearly (if at all) is that your "nodelist" variable is dynamic, meaning that changing the DOM node tree affects your variable (it might seem odd but that's the way it works).

Here is a simplified example that does what your script is trying to do, which his to give all filter attributes that currently have a value of "filter5" a value of "" (empty string).

Code:
var nodelist = ActiveDocument.getNodesByXpath("//*[@filter='filter5']");
var nodecount = nodelist.length;
Application.Alert("Fixing " + nodecount + " nodes...");
for (i=0; i<nodecount; i++) {
nodelist.item(0).setAttribute("filter", "");
}

Note: I assume you specifically wish to set the value to an empty string, rather than removing the attribute (which is technically different and will be treated differently by some XML processors). If you want to remove the attribute instead you can use this: nodelist.item(0).removeAttribute("filter")
Logged
russurquhart1
Member

Posts: 46


« Reply #2 on: May 05, 2009, 04:19:06 PM »

That did the trick!

As i was experimenting, i was looking at the length noticing that each time through the loop, that number was decreasing, and i couldn't figure out why.

So is the dynamic nature of the node list tied to the initial Xpath declaration, i.e. as i changed the attribute to "" the number of nodes in the node list decreased? Is that where the dynamic nature comes in?

Thanks,


Russ
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #3 on: May 05, 2009, 06:26:37 PM »

It is tied more directly to your nodelist object (variable) and the values it contains, so that (in theory) if you were to iterate through the list of nodes once and set the attribute values, then iterate through a second time and read the attribute values the read operation would reflect the current state after the first iteration (not the original state) of the document's nodes. The confusion that most people might have with this is that you are not dealing with things purely in script here, you are actually making real modifications to the document.
Logged
russurquhart1
Member

Posts: 46


« Reply #4 on: May 06, 2009, 08:32:39 AM »

Not to beat a dead horse, but trying to get my mind around this. In a previous script i wrote something like:

 var nodelist = ActiveDocument.getNodesByXPath("//entry[@role='hdr']/ancestor::GenTable[1]");

       var current = 0;
       var number_corrected = 0;
       while (current < nodelist.length)
       {
         var current_node = nodelist.item(current);
          current_node.setAttribute("role", "Register");
          current_node.setAttribute("columns", "1");
          number_corrected = number_corrected + 1;
          current = current + 1;
       }

I iterate through the nodelist, and this works. (From what you said previously, i would think it wouldn't.) Is this list not affected because the nodes that were the basis for the selection, their attribute values, remain unchanged and only attribute values for their ancestors are changed?

Sorry still a little confused.

Thanks,


Russ
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #5 on: May 14, 2009, 02:47:11 PM »

Our DOMNodeList object implements a "live" list. The W3C XPath specification recommends not to do this but doesn’t preclude us from doing it. For XMetaL it was the best solution (there are other features we support involved here that are affected as well).

The property DOMNodeList.length is also "live" and re-evaluates every time the property is accessed (as you have seen).
Logged
frances
Member

Posts: 1


« Reply #6 on: March 18, 2015, 11:22:06 AM »

I'm also pretty confused about DomNodeList.
I'm trying to iterate through a node list and change the type of each element found. With my current approach, the script misses every other element, so only half of the elements in a doc get changed.

My current approach was to get inside the element's contents, then change its container name. (sorry for the horrible following formatting:)

Code:
//create a Range object to manipulate
    rngText = ActiveDocument.Range;

foundTagsList = ActiveDocument.getElementsByTagName("draft-comment");
    for (var j=0; j < foundTagsList.length; j++){
        element = foundTagsList.item(j);
         //convert the node into a selection (using Range)
         rngText.SelectNodeContents(element);
         //get the node's contents w/o containing tags
         rngText.SelectContainerContents();
         if (rngText.CanChange("note")) {
           rngText.ContainerName = "note";
            }
      }

   

background: my goal is to turn each <draft-comment> into a certain type of <note> so that in my PDF output, reviewers can see my questions. (I'll also later turn all those types of notes back into draft-comments)
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #7 on: March 18, 2015, 01:15:25 PM »

@frances

I think to do what you want to do you would have to change the nodes in reverse order (reverse the loop).

However, I think in this particular case the following script will run quite a bit faster:

Code:
//XMetaL Script Language JScript:
rng = ActiveDocument.Range;
rng.MoveToDocumentStart();
while(rng.MoveToElement("draft-comment")) {
if(rng.CanChange("note")) {
rng.ContainerName = "note";
}
}
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #8 on: March 18, 2015, 01:28:09 PM »

Since each change is causing the document to update it would make it even faster (and stop flickering) to turn off document updating at the start and end. This issue will be more noticeable when there are many draft-comment elements in a larger document.

Code:
//XMetaL Script Language JScript:
ActiveDocument.FormattingUpdating = false;
rng = ActiveDocument.Range;
rng.MoveToDocumentStart();
while(rng.MoveToElement("draft-comment")) {
if(rng.CanChange("note")) {
rng.ContainerName = "note";
}
}
ActiveDocument.FormattingUpdating = true;
Logged
Derek Read
Program Manager (XMetaL)
Administrator
Member

Posts: 2621



WWW
« Reply #9 on: March 18, 2015, 02:05:09 PM »

@frances

Here's your original, rewritten:

Code:
ActiveDocument.FormattingUpdating = false;
var rngText = ActiveDocument.Range;
foundTagsList = ActiveDocument.getElementsByTagName("draft-comment");
while (foundTagsList.length > 0){
element = foundTagsList.item(0);
//convert the node into a selection (using Range)
rngText.SelectNodeContents(element);
//get the node's contents w/o containing tags
rngText.SelectContainerContents();
if (rngText.CanChange("note")) {
rngText.ContainerName = "note";
}
}
ActiveDocument.FormattingUpdating = true;

I've compared the length of time it takes to run both on the same document (an extreme case containing 1,000 draft-comment elements). The simplified version that does not use getElementsByTagName runs about 18% faster (13.6 seconds vs 11.1 seconds) and so not a big difference. For a much shorter and smaller document (which if this is DITA is almost certain to be the case) it would probably not be noticeable.

Looking at it again though, I don't think there's much point in doing the CanChange check. If the call to setting the ContainerName (used to change the element) was to fail then the nodelist would not get any smaller and so the loop would never finish (either as a while or a for loop). I think using this logic you'd probably want to bail out of the loop instead (if there is a possibility of failure) or it would need to be rewritten.
Logged
Pages: 1
Print
Jump to: