Gerico
Gerico

Reputation: 5169

Javascript dynamically set width based on number in class name

I have a table and within each cell with reside one single div with a class that is prefixed with perc- and will contain on number ranging from 0 to 100. For instance perc-60 which would equate to 60%.

I can do this in CSS by generating a SASS loop and processing 100 variants on the perc- class. For purposes of learning I'd like to know how I can achieve an inline style via Javascript where by I can set the width of the div based on the number in the class. The numbers get applied via a backend system out of my control, but will related to some data from the user.

Example markup:

<table>
    <tr>
        <td class="perc-60"><div></div></td>
    </tr>
    <tr>
        <td class="perc-15"><div></div></td>
    </tr>
    <tr>
        <td class="perc-45"><div></div></td>
    </tr>
    <tr>
        <td class="perc-16"><div></div></td>
    </tr>
    <tr>
        <td class="perc-88"><div></div></td>
    </tr>
    <tr>
        <td class="perc-79"><div></div></td>
    </tr>
    <tr>
        <td class="perc-98"><div></div></td>
    </tr>
</table>

At the moment I use a SASS loop to go through all the classes and target the divs width within the td.

Upvotes: 1

Views: 2279

Answers (3)

zer00ne
zer00ne

Reputation: 44043

I got carried away, I made it fancy, sorry. I used JavaScript as originally requested. There are comments for each step of the script.

	var td = selArray('td'); // Make an array of <td> selectors
	for (var i = 0; i < td.length; i++) { // Loop thru array
	  var perc = td[i].className; // Find each <td> class
	  //console.log('Cell '+i+': '+perc);
	  var cell = document.querySelector('.' + perc); // Create DOM Object for <td>
	  //console.log(cell.className);
	  var div = cell.querySelector('div'); // Create DOM Object for <td> > <div>
	  var str = perc.split('-').pop(); // Strip 'perc-' from class, now a String "number" remains
      /* http://stackoverflow.com/a/3568968/2813224 */
	  var divWidth = str + "%"; // Add a "%" to String "number"
	  //console.log(divWidth);
	  div.style.width = divWidth; // Assign String "number" as <div> width
	  //console.log(div.style.width); 
	  div.innerHTML = divWidth; // Insert width as text into <div>
	}
	/* This function will accept a selector (ex. #elementID, .elementCLASS, elementTAGNAME, etc.) like jQuery does and then returns an array of selectors that matched. 
    https://developer.mozilla.org/en-US/docs/Web/API/NodeListhttps://developer.mozilla.org/en-US/docs/Web/API/NodeList */
	function selArray(sel) {
	  var eleArr = Array.prototype.slice.call(document.querySelectorAll(sel));
	  return eleArr;
	}
html {
  box-sizing: border-box;
  font: 900 16px/1.5'Source Code Pro';
}
*,
*:before,
*:after {
  box-sizing: inherit;
  margin: 0;
  padding: 0;
  border: 0;
}
body {
  height: 100vh;
  width: 100vw;
  background: #666;
}
table.x {
  padding: 0;
  box-shadow: 0 1px 9px 1px #ccc;
  border-radius: 6px;
  margin: 20px auto;
  width: 80%;
  table-layout: fixed !important;
}
.x th {
  color: #FFF;
  background: #086ac8;
  padding: 10px;
  text-align: center;
  vertical-align: middle;
  height: 2em;
}
.x tr:nth-child(odd) {
  background-color: #333;
  color: #FFF;
}
.x tr:nth-child(even) {
  background-color: #2e90ef;
  color: #333;
}
.x td {
  border-style: solid;
  border-width: 1px;
  border-color: #57acff;
  padding: 5px;
  text-align: left;
  vertical-align: middle;
  height: 2em;
}
thead th:first-child {
  border-top-left-radius: 6px;
}
thead th:last-child {
  border-top-right-radius: 6px;
}
.x tbody tr:last-child th:first-child {
  border-bottom-left-radius: 6px;
}
.x tbody tr:last-child td:first-child {
  border-bottom-left-radius: 6px;
}
.x tbody tr:last-child td:last-child {
  border-bottom-right-radius: 6px;
}
.x td div {
  height: 1.5em;
  outline: 1px solid #FC0;
  background: hsla(60, 100%, 50%, .3);
  vertical-align: middle;
}
<table class='x'>
  <thead>
    <tr>
      <th></th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td class="perc-60">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-15">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-45">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-16">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-88">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-79">
        <div></div>
      </td>
    </tr>
    <tr>
      <td class="perc-98">
        <div></div>
      </td>
    </tr>
  </tbody>
</table>

Upvotes: 1

user663031
user663031

Reputation:

Classes are abstractions which can be re-used across elements and easily bring in sets of properties. This is more modular and maintainable, since the classes can be changed or added to and the changes automatically propagate to all the elements that use them.

There is also a CSS architectural style which involves classes with very small number, even just one, property ("micro-classes"). In this case, it is not about modularity or ability to change the class; it is more a matter of syntactic sugar and compactness. For instance, I can define a class absolute which is defined as .absolute { position: absolute; }, then apply it to an HTML element by simply saying class='absolute', instead of style='position: absolute; '.

In your case, there is no advantage of using classes, especially if you plan to introduce additional preprocessor machinery to generate all of them. What you propose is exactly equivalent to merely putting a style='width: 60%; ' attribute on the element. And that is precisely what you should do.

Putting in-line style attributes directly on HTML elements is not "evil", in the sense that eval is, for example. It's a practice which has been deprecated to help encourage people to write modular, orthogonal style rules independent of the HTML. However, there's absolutely nothing wrong with it if the style is specific to the particular element. In fact, in such cases, it can be be considered bad design to separate and externalize classes whose only purpose is to assign one or more properties to a specific HTML element.

Upvotes: 0

Lucas Rodrigues
Lucas Rodrigues

Reputation: 1192

Here is a jQuery solution that would iterate through the td's and use the class as a parameter:

Hopefully your backend is already outputting single-digit values preceded by a zero.

$(document).ready(function(){
    $("td").each(function(){
        $(this).width($(this).attr("class").substring(5,7) + "%");
    });
});

Make sure your tds are already using the right box-sizing:

td {
   box-sizing: border-box;
}

Upvotes: 0

Related Questions