Nick Chapman
Nick Chapman

Reputation: 4634

Is there an equivalent of elem.innerHTML that gets the code in the tag body?

If I have the following:

<p class="demo" id="first_p">
    This is the first paragraph in the page and it says stuff in it.
</p>

I could use

document.getElementById("first_p").innerHTML

to get

This is the first paragraph in the page and it says stuff in it. 

But is there something simple you can run which would return as a string

class="demo" id="first_p"

I know I can iterate through all of the element's attributes to get each one individually but is there a function which returns tagHTML or something like that?

Upvotes: 1

Views: 175

Answers (5)

The Spooniest
The Spooniest

Reputation: 2873

The following code is something of a mouthful: I wrote it as a one-liner, but I've broken it out into several lines here. But this will get you a plain object where the keys are attribute names and the values are the values of the corresponding attributes:

Array.prototype.reduce.call(
    document.getElementById('first_p').attributes,
    function (attributes, currentAttribute) {
        attributes[currentAttribute.name] = currentAttribute.value;
        return attributes;
    },
    {}
);

Going through this, document.getElementById('first_p').attributes gets you a NamedNodeMap of the element's attributes. A NamedNodeMap is not an Array, but Array.prototype.reduce.call(...) calls Array.prototype.reduce on the NamedNodeMap as if it were an Array. We can do this because NamedNodeMap is written so that it can be accessed like an array.

But we can't stop here. That NamedNodeMap that I mentioned is an array of Attr objects, rather than an object of name-value pairs. We need to convert it, which is where the other arguments to Array.prototype.reduce come into play.

When it's not being called in a strange way, Array.prototype.reduce takes two arguments. The second argument (which is third for us because of the way we called it) is an object that we want to build up. In our case, that's a brand-new bare object: the {} that you see at the end.

The first argument to Array.prototype.reduce (which, again, is second for us) is another function. That function will get called once for each item in the loop, but it takes two arguments. The second argument is the current loop item, which is easy to understand, but the first argument is a little wild. The first time we call that function, its first argument is the object we want to build up (i.e. the last argument to Array.prototype.reduce. Each time after that, the first argument is whatever that function returned the last time it was called. Array.prototype.reduce returns whatever the last call to its inner function returned.

So we start with an empty object. Then for every Attr in the element's attributes, we add something to the object, and return it. When the last call finishes, the object is finished, so we return that. And this is how we make the attribute list.

If you wanted the exact code in the tag, like a String, then I'm afraid there is no standard function to get that exactly. But we can get a close approximation of that code, with a similar setup:

Array.prototype.map.call(
    document.getElementById('first_p').attributes,
    function (currentAttribute) {
        return currentAttribute.name + '=' + JSON.stringify(currentAttribute.value);
    }
).join(' ');

The basic principle is the same: we take that NamedNodeMap and call an Array function on it, but this time we're using map instead of reduce. You can think of map as a special case of reduce: it always builds up an Array, with one element for every element that was in the original. Because of that, you don't even need to mention the object you're building up: the callback function only has one argument, and we just return the thing to put into the new Array. Once we're done, we have an Array of 'name="value"' strings, and then we just join that with ' '.

Upvotes: 1

David Thomas
David Thomas

Reputation: 253318

Well, while nothing currently exists to do this directly (though the approaches using the Node's attributes is a more reliable approach, one option is to create this method yourself:

HTMLElement.prototype.tagHTML = function(){
    // we create a clone to avoid doing anything to the original:
    var clone = this.cloneNode(),
    // creating a regex, using new RegExp, in order to create it
    // dynamically, and inserting the node's tagName:
        re = new RegExp('<' + this.tagName + '\\s+','i'),
    // 'empty' variables for later:
        closure, str;

    // removing all the child-nodes of the clone (we only want the
    // contents of the Node's opening HTML tag, so remove everything else):
    while (clone.firstChild){
        clone.removeChild(clone.firstChild);
    }

    // we get the outerHTML of the Node as a string,
    // remove the opening '<' and the tagName and a following space,
    // using the above regular expression:
    str = clone.outerHTML.replace(re,'');

    // naively determining whether the element is void
    // (ends with '/>') or not (ends with '>'):
    closure = str.indexOf('/>') > -1 ? '/>' : '>';

    // we get the string of HTML from the beginning until the closing
    // string we assumed just now, and then trim any leading/trailing
    // white-space using trim(). And, of course, we return that string:
    return str.substring(0,str.indexOf(closure)).trim();
};

console.log(document.getElementById('test').tagHTML());
console.log(document.getElementById('demo').tagHTML());

JS Fiddle demo.

Upvotes: 0

Indranil Mondal
Indranil Mondal

Reputation: 2857

You can try the following:-

      var attributes = '';
            for(var i=0; i<document.getElementById("first_p").attributes.length; i++){
                var attr = document.getElementById("first_p").attributes[i];
                attributes += attr.nodeName+"='"+attr.nodeValue+"' "
            }
            console.log(attributes);

Upvotes: 1

dee-see
dee-see

Reputation: 24078

It isn't a built-in property, but you can use the array-like object attributes to obtain what you're looking for.

Array.prototype.map.call(element.attributes, function (el) {
  return el.name + '="' + el.value + '"';
}).join(' ')

This is assuming a browser that supports the map function. The Array.prototype.map.call part is because attributes is not really an array and does not have a join method, but because it's an array-like JavaScript's dynamism allows us to call map on it anyway.

Example from the current page with the footer div:

var element = document.getElementById('footer')
Array.prototype.map.call(element.attributes, function (el) {
  return el.name + '="' + el.value + '"';
}).join(' ');
// "id="footer" class="categories""

Upvotes: 1

Jnatalzia
Jnatalzia

Reputation: 1687

You can use document.getElementById("first_p").attributes to get an array of all the attributes on that DOM element

If you wanted them all in one string just do: document.getElementById("first_p").attributes.join(' ') to get the desired output

Upvotes: 0

Related Questions