3

as the title says: Why does jQuery not remove all the data attributes?

<div data-subject-name="Spanisch" data-subject-id="9" data-subject-alias="Spa" data-category-id="1"></div>
$.fn.removeAttrs = function(regex) {
    var regex = new RegExp(regex, "g");
    return this.each(function() {
        var _this = $(this);

        console.log(this.attributes);
        $.each(this.attributes, function(i, attrib){
            console.log(attrib);
            if (attrib && attrib.specified && regex.test(attrib.name)) {
                console.log(attrib.name);
                _this.removeAttr(attrib.name);
            }
        });
    });
};

$('div').removeAttrs('^(data-)');

here is the http://jsfiddle.net/g2pXt/8/

i am using the snipped from Remove multiple html5 data-attributes with jquery @Mathias Bynens but it is not working. so whats the problem of this solution?

Community
  • 1
  • 1
JuKe
  • 663
  • 2
  • 7
  • 20
  • If you watch the console, you'll see an error (in chrome). first fix the errors. Next if you watch the output of your console.logs, you'll see that some of them get removed and some of them don't. – Kevin B Sep 20 '13 at 18:50
  • Works if you change it to jQuery-1.9.1. Not sure why it doesn't work with 1.10.1. Gives me an access denied error in the `SetDocument()` (I'm using IE 9) – Pete Sep 20 '13 at 18:53
  • i updated the jdfiddle... so not error for me in firefox and chrome. @Pete: not working for me with 1.9.1 – JuKe Sep 20 '13 at 18:53
  • Anyway what's the purpose? Why not just remove the associated DATA values? – MightyPork Sep 20 '13 at 18:54
  • @MightyPork: remove all the data attributes without using the data name – JuKe Sep 20 '13 at 18:56
  • 1
    possible duplicate of [Remove multiple html5 data-attributes with jquery](http://stackoverflow.com/questions/8968767/remove-multiple-html5-data-attributes-with-jquery) – MightyPork Sep 20 '13 at 18:59
  • @MightyPork: the solution from Mathias Bynens does not work. but it seems to me that the code is alright. the solution from Andreas Carlbom is working but no so flexible – JuKe Sep 20 '13 at 19:03

2 Answers2

6

You actually have two problems with your code, each of which is partially masking the other.

"test called multiple times on the same global regular expression instance will advance past the previous match." As a result, every other time you performed .test using the same regex, it wasn't searching from the beginning of the string. I replaced regex.test(str) with str.search(regex)>=0 to solve this problem.

In addition, your script seemed to have indexing problems because you were removing attributes in the middle of the loop. I believe this is because "Arrays and array-like objects with a length property...are iterated by numeric index, from 0 to length-1." Removing the attributes all at once after the loop solved the problem (.removeAttr() will accept a space-separated list of attributes to remove.)

$.fn.removeAttrs = function(regex) {
    var regex = new RegExp(regex, "g");
    return this.each(function() {
        var _this = $(this);
        var removethese = '';
        $.each(this.attributes, function(i, attrib){
            if (attrib && attrib.specified && attrib.name.search(regex)>=0) {
                removethese += ' '+attrib.name;
            }
        });
        _this.removeAttr(removethese);
    });
};

http://jsfiddle.net/mblase75/YHyjC/


Note that using .removeAttr() in this way is effectively repeating the loop a second time, so for maximum efficiency, you should retool your code and use a for loop that counts backwards through this.attributes and removes them at the same time. However, for a single short set of attributes, the performance gain will be minimal.

$.fn.removeAttrs = function(regex) {
    var regex = new RegExp(regex, "g");
    return this.each(function() {
        var _this = $(this);
        for (var i=this.attributes.length-1; i>=0; i--){
            var attrib = this.attributes[i];
            if (attrib && attrib.specified && attrib.name.search(regex)>=0) {
                _this.removeAttr(attrib.name);
            }
        }; // end for
    });
};

http://jsfiddle.net/mblase75/Zm4qR/

Blazemonger
  • 90,923
  • 26
  • 142
  • 180
2

Your inner loop is iterating over a list of items that's changing underneath it.

The safest route is to use a straight-JS loop, from the end of the attributes list backward, so elements aren't skipped when a previous element has been deleted:

for ( var i = this.attributes.length - 1; i >= 0; --i ) {
  var attrib = this.attributes[i];

  if (attrib && attrib.specified && regex.test(attrib.name)) 
  {
    console.log(attrib.name);
    _this.removeAttr(attrib.name);
  }
}

Updated jsFiddle, including simplified regex: http://jsfiddle.net/g2pXt/36/

Paul Roub
  • 36,322
  • 27
  • 84
  • 93