简体   繁体   中英

Iterate on HTML table rows using jQuery and change column value

I have a table which looks like this :

在此输入图像描述

I need to iterate on the table rows, but want to work only on the "Price" column . If the price is higher then 20 I need to change the price currency to American dollar (30$ for example) and if the price is lower then 20 change the text color to red.

My table doesn't have an id and i am not really sure how to iterate on the table working on the Price column only and change the color and currency of them. I would be glad if I get some help .

Here is my code for now:

 $(document).ready(function () {
        $("table").find('tr').each(function(i,el){
            var tds = $(this).find('td');
            if (tds.eq(5).text() > "20") {
                tds.eq(5).text() = tds.eq(5).text() + "$";       
            }
            else {
                // here i am not sure how to change the color of the text..
            }

        });
    });

You could use .css() function to set the style like :

if (tds.eq(5).text() > "20") {
     tds.eq(5).text() = tds.eq(5).text() + "$";       
}else{
    tds.eq(5).css('color', 'red');
}

Or you could use the selector directly like :

$('td:eq(5)', this).css('color', 'red');

You could omit the first each() that loops over the table rows by using the :nth-child() selector

 $('table td:nth-child(2)').each(function() { let $this = $(this); // make sure it's a number if (!isNaN($this.text())) { // convert to number and check value if (+$this.text() > 20) { $this.text($this.text() + '$'); } else { $this.css('color', 'red'); } } }); 
 th { background: lightBlue; } th, td { border: 1px solid black; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <table> <tr> <th>Column</th> <th>Price</th> </tr> <tr> <td>Column</td> <td>19</td> </tr> <tr> <td>Column</td> <td>21</td> </tr> </table> 

Ideally, you can add a class to that column (I don't know if this is hardcoded stuff or you're iterating over a list, but will assume the latter) and then the problem is solved.

$(document).ready(function() {
   $("table .price").each(function(i, el) {
      if($(this).text() > "20")
         $(this).text() = $(this).text() + '$';
      else
         $(this).css('color', 'red);
   })
});

While you've already accepted an answer, I wanted to take the time to show you some alternatives approaches; however, first, I'd like to answer your question as you wrote it, with the caveat that in your question you wrote:

If the price is higher then 20 I need to change the price currency to American [dollars] (30$ for example) and if the price is lower [than] 20 change the text color to red.

You don't clarify what should happen if the price is equal to $20, so I'm opting to believe that if the price is less-than 20, the color should be red and if it's equal-to, or greater-than, 20 then the currency should be set. Although, I'm also choosing to set the currency symbol ahead of the price, to give $30 rather than – as you wrote in the question – 30$ , in line with the the observed custom of the US.

So, to explicitly answer your question:

// finding the <td> elements that are the 6th (one-based CSS
// indexing) child of their <tr> parent, that <tr> contained
// within a <tbody> elementl
$('tbody tr td:nth-child(6)')

  // using the css() method to update the style(s) of those
  // elements:
  .css({

    // updating the 'color' property of the current element
    // using the anonymous function:
    'color': function(index, currentValue) {

      // here 'this' is the current <td> of the collection of
      // <td> elements; we find the current <td> element's
      // textContent, trim it of leading or trailing white-
      // space, using String.prototype.trim(), and parse that
      // textContent using parseFloat() (since prices are
      // not necessarily integers).
      // If the parsed number is less than 20 the 'red' string
      // is returned as the property-value for the 'color'
      // property, otherwise we return the currentValue held for
      // the color property:
      return parseFloat(this.textContent.trim()) < 20 ? 'red' : currentValue;

      // note that we can, instead, return an empty string
      // which would cause the 'color' property not to be
      // set (as a non-valid value causes the rule to be
      // discarded); this could be better for your use case
      // and will allow the usual CSS cascade to apply.

  }

// we then chain with the text() method:
}).text(function(index, currentValue) {

  // here we again parse the trimmed textContent of the
  // current <td> (here the currentValue is the current
  // textContent, so does not have to be retrieved via
  // this.textContent) to a value and test if that value
  // is less than 20. If the value *is* less than 20, we
  // return that value unchanged; if it is *not* less than
  // 20 we prefix the currentValue with a '$' symbol, and
  // return the concatenated string:
  return parseFloat(currentValue.trim()) < 20 ? currentValue : '$' + currentValue;
});

 $('tbody tr td:nth-child(6)').css({ 'color': function(index, currentValue) { return parseFloat(this.textContent.trim()) < 20 ? 'red' : currentValue; } }).text(function(index, currentValue) { return parseFloat(currentValue.trim()) < 20 ? currentValue : '$' + currentValue; }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>20</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

All of the above is, of course, easily achieved in plain JavaScript as below:

// here we use Function.prototype.call() to apply the
// Array.prototype.slice() method to the array-like
// NodeList returned by document.querySelectorAll():
Array.prototype.slice.call(
  document.querySelectorAll('tbody tr td:nth-child(6)')

// using Array.prototype.forEach() to iterate over the
// Array of <td> nodes:
).forEach(function(cell, index) {

  // we use String.prototype.trim() to remove leading
  // and/or trailing white-space from current <td> element's
  // textContent, and parse that into a number using
  // parseFloat:
  let currentValue = parseFloat(cell.textContent.trim());

  // if that number is less than 20:
  if (currentValue < 20) {

    // we set the 'color' of the <td> element to 'red':
    cell.style.color = 'red';
  } else {

    // otherwise we return the existing textContent
    // with a preceding '$':
    cell.textContent = '$' + cell.textContent

    // because parseFloat() - and parseInt() - parse
    // the numbers from the beginning of a string until
    // the first non-numeric character we can't rely on
    // currentValue being equal to the complete textContent
    // of the <td>, which is why we return
    //   '$' + cell.textContent
    // instead of
    //   '$' + currentValue

  }
});

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell, index) { let currentValue = parseFloat(cell.textContent.trim()); if (currentValue < 20) { cell.style.color = 'red'; } else { cell.textContent = '$' + cell.textContent; } }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>20</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

Obviously if your users have access to ES6-compliant browsers, then the above can be rewritten using Array.from() and Arrow functions (since we don't access, use or require this within the functions) as:

// converting the Array-like NodeList returned by
// document.querySelectorAll() into an Array:
Array.from(
  document.querySelectorAll('tbody tr td:nth-child(6)')

// using Arrow function syntax to write the anonymous function
// Array.prototype.forEach():
).forEach(

  // 'cell' refers to the current <td> of the array of <td>
  // elements over which we're iterating:
  cell => {

    // here, again, we parse the trimmed textContent of the
    // current <td> to a number:
    let currentValue = parseInt(cell.textContent.trim());

    // if the currentValue is less than 20:
    if (currentValue < 20) {

      // the cell's color is set to 'red':
      cell.style.color = 'red';
    } else {

      // otherwise we preface the cell's textContent
      // with a '$' symbol:
      cell.textContent = '$' + cell.textContent;
    }
  }
);

 Array.from( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach( cell => { let currentValue = parseInt(cell.textContent.trim()); if (currentValue < 20) { cell.style.color = 'red'; } else { cell.textContent = '$' + cell.textContent; } } ); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>20</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

Having written and, I hope, adequately explained the above approaches to solving the problem as written what follows is my suggestion that you should, instead, use classes style the cell contents, rather than directly updating the color or text.

This approach requires adding appropriate classes to your CSS, for example I've added:

.lessThan {
  color: red;
}
.currency::before {
  content: '$';
}

To give, with jQuery:

// selecting the relevant elements (as above), and chaining
// with the addClass() method:
$('tbody tr td:nth-child(6)').addClass(function() {

  // here we trim the textContent of leading/trailing white-space
  // parse that String into a number and, if that number is
  // less-than 20 we return the 'lessThan' string as the class-name
  // to add, otherwise we return the 'currency' class-name:
  return parseFloat(this.textContent.trim()) < 20 ? 'lessThan' : 'currency';
});

 $('tbody tr td:nth-child(6)').addClass(function() { return parseInt(this.textContent.trim(), 10) <= 20 ? 'lessThan' : 'currency'; }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } .lessThan { color: red; } .currency::before { content: '$'; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>20</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

The above, rewritten in JavaScript, becomes:

// converting the array-like NodeList from
// document.querySelectorAll() into an Array:
Array.prototype.slice.call(
  document.querySelectorAll('tbody tr td:nth-child(6)')

// using Array.prototype.forEach() to iterate over
// that Array:
).forEach(function(cell) {

  // 'cell' refers to the current <td> of the Array
  // of <td> elements over which we're iterating;
  // here we use the Element.classList API to add
  // a class-name to the current element:
  cell.classList.add(

    // a ternary operator to assign the 'lessThan' class if
    // the parsed number is less than 20, or the 'currency'
    // class if it is not:
    parseFloat(cell.textContent.trim()) < 20 ? 'lessThan' : 'currency'
  );

});

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell) { cell.classList.add( parseFloat(cell.textContent.trim()) < 20 ? 'lessThan' : 'currency' ); }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } .lessThan { color: red; } .currency::before { content: '$'; } 
 <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>20</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

The benefit of using classes to modify the presentation of the elements is that it's so much easier to un-modify those elements, rather than having to unset the color property-value or un-modify the text-content of the Node; also, obviously, it allows for multiple different properties to be adjusted more easily should requirements change in the future.

It's also worth noting that, in all the above snippets of code there is a simple if / else , and no guard against the cells over which we're iterating having non-numeric values, for example:

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell, index) { let currentValue = parseFloat(cell.textContent.trim()); if (currentValue < 20) { cell.style.color = 'red'; } else { cell.textContent = '$' + cell.textContent; } }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>Two hundred</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

This can, of course, be added via the use of an additional else if check, to give, for example:

  cell => {

    // here, again, we parse the trimmed textContent of the
    // current <td> to a number:
    let currentValue = parseInt(cell.textContent.trim()),

        // here we use the isNan() function to check
        // whether the supplied value is not-a-number
        // (which is true if the value is not a valid
        // number), and then we negate that result so
        // assigned variable-name makes sense (and to
        // avoid negation in the assessments of the
        // if/else if):
        isNumber = !isNaN( currentValue );

    // if the currentValue is less than 20:
    if (isNumber && currentValue < 20) {

      // the cell's color is set to 'red':
      cell.style.color = 'red';
    } else if (isNumber) {

      // otherwise we preface the cell's textContent
      // with a '$' symbol:
      cell.textContent = '$' + cell.textContent;
    } else {
      // this 'else' is not required, unless you
      // wish to modify the element in some way
      // when neither of the above assessments
      // are satisfied
    }
  }

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell, index) { let currentValue = parseFloat(cell.textContent.trim()), isNumber = !isNaN(currentValue); if (isNumber && currentValue < 20) { cell.style.color = 'red'; } else if (isNumber) { cell.textContent = '$' + cell.textContent; } }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>Two hundred</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

In those situations where a ternary was used, though, the re-writing – if simplicity is a goal – would be much as above, except a variable would be assigned to contain the relevant, or no, value:

Array.prototype.slice.call(
  document.querySelectorAll('tbody tr td:nth-child(6)')
).forEach(function(cell) {
  let value = parseFloat(cell.textContent.trim()),
    isNumber = !isNaN(value),

    // initialising the name of the class to be added
    // as an undefined variable:
    classToAdd;

  if (isNumber && value < 20) {
    classToAdd = 'lessThan';
  } else if (isNumber) {
    classToAdd = 'currency';
  }

  // as an empty string is not a valid argument for
  // Element.classList.add(), we test that the
  // classToAdd variable is truthy (ie not undefined,
  // false, null, zero-length string, 0...):
  if (classToAdd) {

    // and if it is, we then add the class:
    cell.classList.add(classToAdd);
  }

});

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell) { let value = parseFloat(cell.textContent.trim()), isNumber = !isNaN(value), classToAdd; if (isNumber && value < 20) { classToAdd = 'lessThan'; } else if (isNumber) { classToAdd = 'currency'; } if (classToAdd){ cell.classList.add(classToAdd); } }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } .lessThan { color: red; } .currency::before { content: '$'; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>Two hundred</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

Above I said that " if simplicity is a goal – would be much as above, except a variable would be assigned... " though it is possible – but thoroughly inadvisable – to use a multiple-part ternary operator 1 to avoid the use of a simple, easy-to-read and easily-maintainable if / else if :

Array.prototype.slice.call(
  document.querySelectorAll('tbody tr td:nth-child(6)')
).forEach(function(cell) {
  cell.classList.add(

    // beyond the overly complex composition there is the additional
    // problem that an empty-string is not a valid argument to
    // Element.classList.add(), therefore a specified value must
    // be provided; here we use undefined, but that will be coerced
    // to a string ('undefined'), which will show up in the DOM as
    // a class-name:
    parseFloat(cell.textContent.trim()) < 20 ? 'lessThan' : !isNaN(parseFloat(cell.textContent.trim())) ? 'currency' : undefined
  );

});

 Array.prototype.slice.call( document.querySelectorAll('tbody tr td:nth-child(6)') ).forEach(function(cell) { cell.classList.add( parseFloat(cell.textContent.trim()) < 20 ? 'lessThan' : !isNaN(parseFloat(cell.textContent.trim())) ? 'currency' : undefined ); }); 
 *, *::before, *::after { box-sizing: border-box; margin: 0; } table { width: 100vw; } .lessThan { color: red; } .currency::before { content: '$'; } 
 <table> <thead> <tr> <th>PersonID</th> <th>LastName</th> <th>FirstName</th> <th>FromDate</th> <th>ToDate</th> <th>Price</th> </tr> </thead> <tbody> <tr> <td>1</td> <td>Davolio</td> <td>Nancy</td> <td>10-11-2011</td> <td>30-12-2010</td> <td>Two Hundred</td> </tr> <tr> <td>2</td> <td>Fuller</td> <td>Andrew</td> <td>12-01-2011</td> <td>30-12-2015</td> <td>30</td> </tr> <tr> <td>3</td> <td>Leverling</td> <td>Janet</td> <td>09-03-2011</td> <td>30-12-2013</td> <td>15</td> </tr> </tbody> </table> 

References:


  1. Only consider this a reasonable approach if the answers to all the following questions are 'no':

    • Do your colleagues know where you live?
    • Do you love, or even like, your colleagues?
    • Do you enjoy evenings, and weekends, at home without having to worry about colleagues finding you?
    • Do you love, or even like, yourself?
    • Do you like, or enjoy, happy working days maintaining easy-to-read code?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM