views:

141

answers:

2

My goal is to remove all <[script]> nodes from a document fragment (leaving the rest of the fragment intact) before inserting the fragment into the dom.

My fragment is created by and looks something like this:

    range = document.createRange();
    range.selectNode(document.getElementsByTagName("body").item(0));
    documentFragment = range.cloneContents();
    sasDom.insertBefore(documentFragment, credit);
    document.body.appendChild(documentFragment);

I got good range walker suggestions in a separate post, but realized I asked the wrong question. I got an answer about ranges, but what I meant to ask about was a document fragment (or perhaps there's a way to set a range of the fragment? hrmmm). The walker provided was:

function actOnElementsInRange(range, func) {
function isContainedInRange(el, range) {
    var elRange = range.cloneRange();
    elRange.selectNode(el);
    return range.compareBoundaryPoints(Range.START_TO_START, elRange) <= 0
            && range.compareBoundaryPoints(Range.END_TO_END, elRange) >= 0;
}

var rangeStartElement = range.startContainer;
if (rangeStartElement.nodeType == 3) {
    rangeStartElement = rangeStartElement.parentNode;
}

var rangeEndElement = range.endContainer;
if (rangeEndElement.nodeType == 3) {
    rangeEndElement = rangeEndElement.parentNode;
}

var isInRange = function(el) {
    return (el === rangeStartElement || el === rangeEndElement ||
                isContainedInRange(el, range))
        ? NodeFilter.FILTER_ACCEPT : NodeFilter.FILTER_SKIP;
};

var container = range.commonAncestorContainer;
if (container.nodeType != 1) {
    container = container.parentNode;
}

var walker = document.createTreeWalker(document,
    NodeFilter.SHOW_ELEMENT, isInRange, false);

while (walker.nextNode()) {
    func(walker.currentNode);
}
}

actOnElementsInRange(range, function(el) {
    el.removeAttribute("id");
});

That walker code is lifted from: http://stackoverflow.com/questions/2690122/remove-all-id-attributes-from-nodes-in-a-range-of-fragment

PLEASE No libraries (ie jQuery). I want to do this the raw way. Thanks in advance for your help

+1  A: 

The easiest way to gather all <script> nodes would be to use getElementsByTagName, but unfortunately that is not implemented on DocumentFragment.

However, you could create a temporary container and append all elements within the fragment, and then go through and remove all <script> elements, like so:

var temp = document.createElement('div');

while (documentFragment.firstChild)
    temp.appendChild(documentFragment.firstChild);

var scripts = temp.getElementsByTagName('script');
var length = scripts.length;

while (length--)
    scripts[length].parentNode.removeChild(scripts[length]);

// Add elements back to fragment:
while (temp.firstChild)
    documentFragment.appendChild(temp.firstChild);
J-P
When I've appended it to existing dom elements, it's executed the javascript. This crashed the browser. I suspect that will happen again, although maybe it won't because temp isn't placed in the dom yet?
Matrym
What if the while loop that did documentFragment.firstChild did a test to see if the nodeName was script, and if so, it didn't append?
Matrym
Appending `<script>`s to out-of-DOM elements shouldn't cause any execution.
J-P
You could do the `while(documentFragment.firstChild)` thing, but it'll only catch scripts that are direct children of the fragment... not nested children -- E.g. `[FRAGMENT]<div><script/></div>[/FRAGMENT]`.
J-P
J-P, awesome solution, thanks!
Matrym
+1  A: 

Correct me if I'm wrong, but if the documentFragment is a real DOM Fragment, you should be able to do something like:

var scripts = documentFragment.getElementsByTagName('script');
if (scripts.length){
  for (var i=0, l = scripts.length;i<l;i++){
    documentFragment.removeChild(scripts[i]);
  }
}

right?

Correction: you can't apply getElementsByTagName to a documentFragment, J-P is right. You can however us a child of the fragment if it is a (cloned) node supporting getElementsByTagName. Here's some (working) code I use within a larger script a few days ago:

var fragment = d.createDocumentFragment(), f;
fragment.appendChild(document.createElement('div'));
fragment.firstChild.appendChild(zoeklijst.cloneNode(true));
f = fragment.firstChild;
return f.getElementsByTagName(getList); //<==
KooiInc
Note J-P's response, which notes get by tagname doesn't work on fragments.
Matrym
See the corrected answer. The $ is not jquery by the way, I use a home brew javascript framework to wrap DOM elements.
KooiInc
Should your homebrew javascript framework be included too then? Where does "zoeklijst" come from?
Matrym
No, you don't need the framework. See edit: Removed the framework stuff. 'Zoeklijst' is a div containing a lot of p elements.
KooiInc