0

From this SO answer he said Object.keys() get properties defined on the object itself (the ones that return true for obj.hasOwnProperty(key) but below code returns 4 keys and also producing an error which is confusing to me.

(function () {
    "use strict";
    var buttons = document.getElementsByClassName('remove');
    console.log("No of keys: ", Object.keys(buttons).length);
    for ( var i in Object.keys( buttons ) ) {
        buttons[i].onclick = function() {
            this.parentElement.remove();
            console.log(this.id);
        };
    }
})();
<div>Some entry here
    <button id="0" class="remove">Remove</button>
</div>
<div>Another entry here
    <button id="1" class="remove">Remove</button>
</div>

how to do this properly in javascript?

Community
  • 1
  • 1
Srinivas
  • 464
  • 3
  • 17
  • `but below code returns 4 keys` nope, 2 in firefox – Jaromanda X Sep 12 '17 at 05:58
  • i am using chrome. – Srinivas Sep 12 '17 at 06:04
  • It's 4 in Chrome and 2 in Firefox. Because the ids are `0` and `1` the HTMLCollection has a collision between the index-based keys and id-based keys. I'm guessing that because the indexes are numbers Chrome is treating them as not equal to the string ids, though `Object.keys` is then converting them all to strings, so you end up with `['0', '1', '0', '1']`. – skirtle Sep 12 '17 at 06:12
  • ewww. chrome returns `Object.keys() == ["0", "1", "0", "1"]` for `document.getElementsByClassName('remove')` - how special is that! - even with the above explanation, why would chrome be so wrong? (note, it has NOTHING to do with the id's being 0 and 1) – Jaromanda X Sep 12 '17 at 06:12
  • 1
    try `for ( var i in Object.keys(Array.from(buttons)))` - chrome doesn't mess that up – Jaromanda X Sep 12 '17 at 06:18
  • @JaromandaX that is also useful. – Srinivas Sep 12 '17 at 06:23

2 Answers2

1

Not only for this true for obj.hasOwnProperty(key) condition, but also one of the properties attributes called enumerable must be also set to true.

What about your code. Lets see what is buttons actually. You see that this is an object, which contains 7 properties. Only 4 properties in Chrome are shown, because they are enumerable

(function () {
    "use strict";
    var buttons = document.getElementsByClassName('remove');
    console.log(buttons);
 
})();
<div>Some entry here
    <button id="0" class="remove">Remove</button>
</div>
<div>Another entry here
    <button id="1" class="remove">Remove</button>
</div>

So when you try to execute this code

(function () {
    "use strict";
    var buttons = document.getElementsByClassName('remove');

    for ( var i in Object.keys( buttons ) ) {
        console.log(i);
    }
 
})();
<div>Some entry here
    <button id="0" class="remove">Remove</button>
</div>
<div>Another entry here
    <button id="1" class="remove">Remove</button>
</div>

it actually get's the indexes of the array returned from the Object.keys which are 1,2,3,4, but you don't have a property with name 2, 3, 4 in your buttons object. So why you get the error. Use forEach function to iterate over each property and add your event listeners.

(function () {
    "use strict";
    var buttons = document.getElementsByClassName('remove');

    Array.prototype.forEach.call(buttons, button => {
        button.addEventListener('click', function() {
            this.parentElement.remove();
            console.log(this.id);
        });
    });
 
})();
<div>Some entry here
    <button id="0" class="remove">Remove</button>
</div>
<div>Another entry here
    <button id="1" class="remove">Remove</button>
</div>
Suren Srapyan
  • 66,568
  • 14
  • 114
  • 112
1

So here's how you'd loop over the buttons from your original code using a for loop:

(function () {
    "use strict";
    var buttons = document.getElementsByClassName('remove');

    console.log(buttons);
    
    for (var index = 0 ; index < buttons.length ; ++index) {
        buttons[index].onclick = function() {
            this.parentElement.remove();
            console.log(this.id);
        };
    }
})();
<div>Some entry here
    <button id="a" class="remove">Remove</button>
</div>
<div>Another entry here
    <button id="b" class="remove">Remove</button>
</div>

Note that the buttons object is an HTMLCollection. This exposes elements by both index and id. In your original example your ids were 0 and 1, which is quite confusing in this case because it causes them to clash with the indexes. Object.keys(buttons) was returning ['0', '1', '0', '1'], which is a bit odd, presumably because of some number/string shenanigans. I've changed the ids in my example to a and b so now Object.keys(buttons) would be ['0', '1', 'a', 'b'].

skirtle
  • 27,868
  • 4
  • 42
  • 57
  • That's actually the correct answer to why it doesn't work, a HTMLCollection exposes the elements both by index and ID, and the OP's strange ID's was misleasing, making it look like the elements where exposed by index twice in Chrome – adeneo Sep 12 '17 at 06:10