"parents().filter" returns undefined if data-attribut equals zero

Viewed 45

This works fine, except when then data-* is equal to "0", can anyone explain why.

If I access the attribute directly it works fine, for example $("#Floor0").data("floor") returns 0 as expected.
But if I use the filter-function to find a parent with data-floor, it will not work if the parents data-floor="0".
If I change the data-floor="0" to data-floor="00", or any other value, it will work.

Link to JSfiddle

console.log($("#Floor2").data("floor"));
console.log($("#Floor1").data("floor"));
console.log($("#Floor0").data("floor"));


Test('a2');
Test('a1');
Test('a0');


function Test(id){

  var FloorId = $("#"+id).parents().filter(function() {
    return $(this).data("floor");
  }).eq(0);
    console.log(FloorId.data("floor"));
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div data-floor="0" id="Floor0">
    <div>
      <label>0</label>
    </div>
    <div>
      <input type="checkbox" id="a0" />
    </div>
  </div>
  <div data-floor="1" id="Floor1">
    <div>
      <label>1</label>
    </div>
    <div>
      <input type="checkbox" id="a1" />
    </div>
  </div>
  <div data-floor="2" id="Floor2">
    <div>
      <label>2</label>
    </div>
    <div>
      <input type="checkbox" id="a2" />
    </div>
  </div>
</div>
<br>
<br>

2 Answers

use closest() instead parents() with a selector to avoid loop here (its faster): so you could use parents with same selector than closest, but parents will test all parents, closest stops when the first is found.

Test("a0");
Test("a1");
Test("a2");

function Test(id) {
  var FloorId = $("#" + id).closest("div[id^=Floor]");
  //parents too
  //var FloorId = $("#" + id).parents("div[id^=Floor]");
  console.log(FloorId.data("floor"));
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div data-floor="0" id="Floor0">
    <div>
      <label>0</label>
    </div>
    <div>
      <input type="checkbox" id="a0" />
    </div>
  </div>
  <div data-floor="1" id="Floor1">
    <div>
      <label>1</label>
    </div>
    <div>
      <input type="checkbox" id="a1" />
    </div>
  </div>
  <div data-floor="2" id="Floor2">
    <div>
      <label>2</label>
    </div>
    <div>
      <input type="checkbox" id="a2" />
    </div>
  </div>
</div>
<br>
<br>

While there are better solutions (already accepted), the reason your initial code didn't work is because:

  • jquery's .data("..") will attempt to coerce the data to the relevant type.
    So data-floor="0" returns (numeric) 0
  • .filter() with return 0 (not return "0") is the same as return false so excludes the item from the filter.

Without making any fundamental changes to your code, you could change .data(..) to .attr("data-..") as .attr() will always return a string.

Updated snippet:

console.log($("#Floor2").data("floor"));
console.log($("#Floor1").data("floor"));
console.log($("#Floor0").data("floor"));


Test('a2');
Test('a1');
Test('a0');


function Test(id){

  var FloorId = $("#"+id).parents().filter(function() {
    return $(this).attr("data-floor");
  }).eq(0);
    console.log(FloorId.data("floor"));
  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
  <div data-floor="0" id="Floor0">
    <div>
      <label>0</label>
    </div>
    <div>
      <input type="checkbox" id="a0" />
    </div>
  </div>
  <div data-floor="1" id="Floor1">
    <div>
      <label>1</label>
    </div>
    <div>
      <input type="checkbox" id="a1" />
    </div>
  </div>
  <div data-floor="2" id="Floor2">
    <div>
      <label>2</label>
    </div>
    <div>
      <input type="checkbox" id="a2" />
    </div>
  </div>
</div>
<br>
<br>

Related