0
\$\begingroup\$

The institution I work for uses an application that has an awful software bug causing useless <code>span</code> tags to appear next to "misspelled" words in iframes.

For instance, HTML that should look like this:

<span class="myClass">Hello! My name is Tabitha.</span>

...might look like this instead:

<span class="myClass">Hello! My name is <span><span><span><span><span>Tabitha.</span></span></span></span></span></span>

My band-aid solution, while we wait for a fix from the application developer, has been to add a script that finds all attribute-less span tags and replaces them with a new <code>textNode</code>. This works because these extra tags are always the direct parent of the <code>textNode</code> containing the misspelled word (or another extra span element). Any formatting or inline styling would occur outside that scope and would thus be unaffected.

But, something tells me there's got to be a better way to do it. Any thoughts?

function fixSpan(iframe) {        
    var doc = iframe.contentDocument;
    var spans = doc.getElementsByTagName('span'); //find all <span> elements
    for (var i = 0; i < spans.length; i++) {
        var span = spans[i];            
        var tag = span.outerHTML.slice(0,6);
        if (tag === '<span>') { //if the span has no attributes
            //replace it with a new textNode containing its textContent
            console.log('FOUND: <span>' + span.textContent + '</span>');
            span.parentNode.replaceChild(document.createTextNode(span.textContent), span);                
        }
    }

    if (doc.documentElement.innerHTML.includes('<span>')) {
        //if any attribute-less tags remain, run the method again
        console.log('Fix not complete, re-running...');
        fixSpan(iframe);
    } else {
        console.log('Fix complete.');
    }
}
\$\endgroup\$
1
  • \$\begingroup\$ What is the harm in leaving the extra tags there? What is the benefit of doing the cleanup? \$\endgroup\$ Commented Jun 14, 2016 at 2:12

1 Answer 1

1
\$\begingroup\$

Some thoughts about your code:

1) You work with the serialized form of the HTML, i.e. with innerHTML and outerHTML. In some (rare) occurrences, it may be acceptable to do so, but most of the time working with the provided API is cleaner. I don't think that the serialized representation is guaranteed to be the same across user agents (attribute ordering, legacy uppercasing of HTML...) - plus what if some inline script declares some '<span>' text... like yours?

2) Re-running the method is a bit dubious, you should be able to remove all spans in a single pass. In your case, the DOM collection is live, so each time you replace a span element, the current index must be shifted back. In itself, manually updating the index of a loop is a bad practice, but Javascript has limited iteration mechanisms, so you have to live with it. It's actually not that unusual in DOM manipulation.

3) Removing the spans without attributes is well and good, but even an empty span may be styled, as part of another selector (e.g. .foo > span). Your fix may bring new problems.

4) Comments should not repeat the code, but bring enlightenment to the reader. Indicating "find all <span> elements" aside a getElementsByTagName is probably unneeded. Indicating why you shift a loop index, if you decide to do it, is necessary.

function fixSpan(doc) {
    var spans = doc.getElementsByTagName("span");
    for (var index = 0; index < spans.length; index++) {
        var span = spans[index];
        if (span.attributes.length == 0) {
            span.parentNode.replaceChild(document.createTextNode(span.textContent), span);
            index--; // Tree is live; removing one span requires shifting the index.
        }
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Regarding your second point: use document.querySelectorAll() which returns a non-live NodeList \$\endgroup\$
    – Andreas
    Commented Jun 14, 2016 at 12:21
  • \$\begingroup\$ @Andreas Thanks for the pointer. I've never used this method, but it looks interesting. \$\endgroup\$
    – Elegie
    Commented Jun 14, 2016 at 12:58

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.