5

How would I sort HTML attributes using JavaScript?

I have this HTML:

<table>
    <tbody>
        <tr>
            <td>Cell 0,0</td>
            <td>Cell 1,0</td>
            <td>Cell 2,0</td>
        </tr>
        <tr>
            <td>Cell 0,1</td>
            <td rowspan="2" colspan="2">Cell 1,1
                <br>Cell 2,1
                <br>Cell 1,2
                <br>Cell 2,2</td>
        </tr>
        <tr>
            <td>Cell 0,2</td>
        </tr>
    </tbody>
</table>

And I want to sort all attribute in all elements into alphabetical order. E.g:

<td colspan="2" rowspan="2">Cell 1,1

The sort function could either be based on a HTML string, or a jQuery object, or a node (it doesn't matter which one).

The reason I need this is because I am doing a diff (with JS, in the browser, after a failed unit test) between 2 sets of HTML and the attribute order is making it fail.

So my questions are:

How can I reorder a nodes attributes?, Or how can I reorder attributes in an HTML string?, Or how can I reorder a jQuery elements attributes?

I haven't got any code for it yet, as I am unsure which method would be the best.

Petah
  • 45,477
  • 28
  • 157
  • 213
  • 13
    More importantly, why do you want to do this? – Colleen Dec 06 '12 at 21:28
  • 3
    What you're describing is pointless and almost nonsensical. Attributes don't have an "order" at all. If you want them ordered in your HTML *source*, then this really doesn't have a lot to do with JavaScript unless your editor/IDE has a built-in JavaScript interpreter. – Pointy Dec 06 '12 at 21:34
  • ^ agreed, to the point of being NARQ =/ – Dagg Nabbit Dec 06 '12 at 21:45
  • This reminds me of people padding their JS so all their code lines up in columns. – Diodeus - James MacFarlane Dec 06 '12 at 22:06
  • @Diodeus ....do you mean like... proper indentation? I don't see how what you're saying is a bad thing. – Colleen Dec 06 '12 at 22:21
  • Don't be silly. I mean when people like to line up their equal signs in a series of assignments when the length of the variable names are different. – Diodeus - James MacFarlane Dec 06 '12 at 22:23
  • 3
    @Diodeus — It's a coding style. There are arguments to be made in its favour. – Quentin Dec 06 '12 at 22:37
  • 1
    @Colleen because I am doing a diff on 2 sets of HTML, and the attribute order is breaking it. – Petah Dec 07 '12 at 00:35
  • 1
    @GGG because I am doing a diff on 2 sets of HTML, and the attribute order is breaking it. – Petah Dec 07 '12 at 00:35
  • that's about the only reasonable reason for doing this. Hope one of the answers helps you (though I'd have to imagine there's an html diff tool out there that handles this) – Colleen Dec 07 '12 at 00:36
  • @Pointy because I am doing a diff on 2 sets of HTML, and the attribute order is breaking it. – Petah Dec 07 '12 at 00:36
  • @Colleen the only good JS diff script I have found is: http://harmen.no-ip.org/javascripts/diff/diff.js http://stackoverflow.com/questions/4462609/looking-for-a-javascript-visual-diff-lib – Petah Dec 07 '12 at 00:38
  • 1
    Petah, I think I'd just write a custom HTML-diff (not text-diff) to deal with this before writing code to (try to) rearrange attributes. Assuming you're trying to do this in the browser, anyway, and the diff is just for display purposes. If you need to generate an actual diff file, not in a web browser, JavaScript just seems like a weird choice... – Dagg Nabbit Dec 07 '12 at 01:04
  • @GGG its for use in a browser, displaying a diff after a failed unit test. Also I think its far easier to use a pre-made text diff, and implement sorting rather than create a HTML diff. – Petah Dec 07 '12 at 06:08
  • This is a Gecko "by-design" bug; I'm not even going to bother finding any report on Bugzilla, half of Mozilla is on illicit drugs. I'm not sure if it's `parseFromString` or `serializeToString` (I suspect the later). I **completely** agree that the rendering engine should *never* change the code (sites may clean up code, optionally of course). Now I have to either implement the working code below or allow others to almost have their way (if not alphabetically). Predictability in code is more important to a developer than browser statism, it's the developer that creates and maintains the code. – John Sep 18 '16 at 13:08
  • Another situation where this may be necessary is when calculating hash codes using an order sensitive algorithm for hash code generation. – Waruyama Feb 27 '18 at 10:22

3 Answers3

3

Have no idea why you need do this with javascript, but it was rather interested. Okay, according spec:

Objects implementing the NamedNodeMap interface are used to represent collections of nodes that can be accessed by name. Note that NamedNodeMap does not inherit from NodeList; NamedNodeMaps are not maintained in any particular order. Objects contained in an object implementing NamedNodeMap may also be accessed by an ordinal index, but this is simply to allow convenient enumeration of the contents of a NamedNodeMap, and does not imply that the DOM specifies an order to these Nodes.

So you could order by changing numeral index, after a while there is my attempt which works smooth in Chrome.

(function(){
  var xpath = '//table/tbody/tr[2]/td[2]',
      attrs = document.evaluate(xpath, document, null, XPathResult.ANY_TYPE, null),
      serializer = new XMLSerializer(),

      el = attrs.iterateNext(),
      act = el.attributes,

      i = act.length,
      _l = i,
      _nodeArr = [],
      _nodeArrKeys = [],
      name;

  console.log(serializer.serializeToString(el));

  for (i ;i--;) {
    name = act[i].nodeName;
    _nodeArr[name] = (act.removeNamedItem(name));
    _nodeArrKeys[i] = name;
  }

  _nodeArrKeys = _nodeArrKeys.sort();
  i++;

  for (i;_l>i;i++) {
    act.setNamedItem(_nodeArr[_nodeArrKeys[i]]);
  }

  console.log(serializer.serializeToString(el));
})();

console log output

<td zataa="123" bbbrowspan="2" cccattr="val" aaacolspan="2">Cell 1,1
                    <br>Cell 2,1
                    <br>Cell 1,2
                    <br>Cell 2,2</td>

<td aaacolspan="2" bbbrowspan="2" cccattr="val" zataa="123">Cell 1,1
                    <br>Cell 2,1
                    <br>Cell 1,2
                    <br>Cell 2,2</td> 
dmi3y
  • 3,482
  • 2
  • 21
  • 32
2

If you really must... try this fiddle.
I have to say though, I'm really curious why you would want to do this.

Code:

var elements = document.getElementsByTagName("*");
sortAttributes(elements);

//From: http://stackoverflow.com/questions/979256/how-to-sort-an-array-of-javascript-objects
function sortBy(field, reverse, primer) {
    var key = function(x) {
        return primer ? primer(x[field]) : x[field];
    };

    return function(a, b) {
        var A = key(a),
            B = key(b);
        return ((A < B) ? -1 : (A > B) ? +1 : 0) * [-1, 1][+ !! reverse];
    }
};

function sortAttributes(elements) {
    for (var j = 0; j < elements.length; j++) {
        var attributes = [];
        for (var i = 0; i < elements[j].attributes.length; i++) {
            attributes.push({
                'name': elements[j].attributes[i].name,
                'value': elements[j].attributes[i].value
            });
        }

        var sortedAttributes = attributes.sort(sortBy('name', true, function(a) {
            return a.toUpperCase();
        }));

        for (var i = 0; i < sortedAttributes.length; i++) {
            $(elements[j]).removeAttr(sortedAttributes[i]['name']);
        }

        for (var i = 0; i < sortedAttributes.length; i++) {
            $(elements[j]).attr(sortedAttributes[i]['name'], sortedAttributes[i]['value']);
        }
    }
}
Anders
  • 15,227
  • 5
  • 32
  • 42
  • 2
    Thanks this works. The reason why I need this is I am doing a diff on 2 sets of HTML, and the attribute order is breaking it. – Petah Dec 07 '12 at 00:50
  • I was just thinking, the only reason I could conceive of doing this is if you were going to run a distance algorithm... and then you confirmed it. :) – Anders Dec 07 '12 at 01:33
  • It may work in some browsers, but browsers are under no obligation to return attributes in the order that they were set. – Pointy Dec 07 '12 at 14:54
  • That's true, but they're under no obligation not to either. That being said, dmi3y probably has the better solution, since it abstracts the problem away from the browser completely, and no doubt runs much faster as well. – Anders Dec 07 '12 at 15:24
  • I was almost about to up-vote but you answered a JavaScript question with a unnecessary library prerequisite. – John Sep 18 '16 at 13:18
1

Just use Prettydiff. It will sort attributes alphabetically when it beautifies your HTML. It is written in JavaScript.

austincheney
  • 1,189
  • 9
  • 11