How specific do you need to be when targeting classes with jQuery?

136 views Asked by At

I have a simple each() that isn't working and in the past what I've found is that sometimes an element is nested too deeply and you need to be more specific. I am trying to find out if depending on certain levels you need divs or other identifiers?

My goal here is to eventually go through every element with the shortpoint-list-item-subtitle and shortpoint-listitem-title class and replace the ISO formatted dates with readable dates via Moment.

<script type="text/javascript">
$( window ).load(function() {
    $('.shortpoint-listitem-subtitle').each(function() {
    var currentElement = $(this);
    var value=currentElement.val();
    console.log(value);     
});
});
</script>

From what I can tell there's nothing about the code that shouldn't work. However like I said .shortpoint-listitem-subtitle may be nested too deeply. The value of value is not being logged in the console.

This is what the selector looks like using Chrome

#shortpoint-gt-3-i-3 > div > div > div.sp-type-file-list-item.sp-attr-connect.shortpoint-dynamic.shortpoint-dynamic-loaded.sp-meta-allow-content.shortpoint-child.shortpoint-dynamic-block.shortpoint-listitem.shortpoint-dynamic-514 > div > div.shortpoint-listitem-content > div.shortpoint-listitem-description

Underlying HTML

https://pastebin.com/ZkXRHNB1

<script type="text/javascript">


var timeout = null;
function waitForDom () {
  console.log("Checking DOM...");
  // check for the elements you expect to exist
  if ($(".shortpoint-listitem-subtitle").length) {
    $(".shortpoint-tab-title").click(function() {waitForDom();});
    clearTimeout(timeout);
    formatDates();
  }
  else {
    // adjust timeout time to whatever feels appropriate to you
    timeout = setTimeout(waitForDom, 500);
  }
}

waitForDom();

function formatDates() {
$('.shortpoint-listitem-subtitle, .shortpoint-listitem-description').each(function() {
  var currentElement = $(this);
  var value=currentElement.text();
  var dateParseRegex = /\d\d\d\d[-]\d\d[-]\d\d[T]\d\d[:]\d\d:\d\d[.]\d{7}[Z]/g;
  var formattedDate = value.replace(dateParseRegex, function (match) {
    return moment(match).format("MMMM Do YYYY, h:mm:ss a");
  });
  currentElement.text(formattedDate);  
});
}

setTimeout(function () {
  var content = '';
  $(".content").append(content);
}, 2300);
</script>
1

There are 1 answers

17
mhodges On BEST ANSWER

The problem is not with your selector, or some "max-depth" limit of DOM traversal (which doesn't exist, by the way), the problem is with your HTML. .shortpoint-listitem-subtitle is a div element. Div elements do not have a .val(). They have .text(), which (currently) you would need to parse out the actual date text from the label text in your function. The other (and I think best) option, is to wrap your actual date in a span and give that a specific class, target that class, and replace the entirety of the text.

Using String.prototype.replace() with a regex pattern to replace the date:

$('.shortpoint-listitem-subtitle, .shortpoint-listitem-description').each(function() {
  var currentElement = $(this);
  var value=currentElement.text();
  var dateParseRegex = /\d\d\d\d[-]\d\d[-]\d\d[T]\d\d[:]\d\d:\d\d[.]\d{7}[Z]/g;
  var formattedDate = value.replace(dateParseRegex, function (match) {
    return moment(match).format("MMMM Do YYYY, h:mm:ss a");
  });
  currentElement.text(formattedDate);  
  console.log("Old Value: ", value);
  console.log("New Value: ", formattedDate);
  console.log("\n");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
<div class="shortpoint-listitem-subtitle" style="color: rgb(163, 180, 156);">Created by: Joe on 2018-02-01T19:20:46.0000000Z</div>
<div class="shortpoint-listitem-description">Last Modified: 2018-02-08T21:14:25.0000000Z by Tom</div>

Using a <span> to hold the contents of each date:

$('.date').each(function() {
  var currentElement = $(this);
  var value=currentElement.text();
  var formattedDate = moment(value).format("MMMM Do YYYY, h:mm:ss a");
  currentElement.text(formattedDate);  
  console.log("Old Value: ", value);
  console.log("New Value: ", formattedDate);
  console.log("\n");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
<div class="shortpoint-listitem-subtitle" style="color: rgb(163, 180, 156);">Created by: Joe on <span class="date">2018-02-01T19:20:46.0000000Z</span></div>
<div class="shortpoint-listitem-description">Last Modified: <span class="date">2018-02-08T21:14:25.0000000Z</span> by Tom</div>

To deal with the uncontrollable asynchronicity of the content loading on your page, you can loop on a timeout until your elements exist, at which time you can format the dates and exit the timeout loop, like so:

var timeout = null;
function waitForDom () {
  console.log("Checking DOM...");
  // check for the elements you expect to exist
  if ($(".date").length) {
    clearTimeout(timeout);
    formatDates();
  }
  else {
    // adjust timeout time to whatever feels appropriate to you
    timeout = setTimeout(waitForDom, 500);
  }
}

waitForDom();

function formatDates() {
  $('.date').each(function() {
    var currentElement = $(this);
    var value=currentElement.text();
    var formattedDate = moment(value).format("MMMM Do YYYY, h:mm:ss a");
    currentElement.text(formattedDate);  
    console.log("Old Value: ", value);
    console.log("New Value: ", formattedDate);
    console.log("\n");
  });
}

// use event delegation to register click handlers on dynamically created elements
$(document).on("click", ".shortpoint-tab-title", function () {
  waitForDom();
});

setTimeout(function () {
  var content = '<div class="shortpoint-listitem-subtitle" style="color: rgb(163, 180, 156);">Created by: Joe on <span class="date">2018-02-01T19:20:46.0000000Z</span></div><div class="shortpoint-listitem-description">Last Modified: <span class="date">2018-02-08T21:14:25.0000000Z</span> by Tom</div>';
  $(".content").append(content);
}, 2300);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.20.1/moment.min.js"></script>
<div class="content"></div>