JavaScript Assigning innerHTML a button, however does not click - javascript

when I assign the
// psuedo code
.innerHTML = [ a button here ]; // see exact code below
it does nothing when clicked. Is there an alternative to doing this? or is it not possible?
the table is dynamically created by JavaScript. As each cell is populated, a criterion is checked - and if met, will produce a button in the following cell. this works and does as intended, however, the button does nothing when clicked.
When checking the source, the button does not show, even though it is there on the HTML page.
I tried elements, that did not work, and I think it is because it is handled outside of the table, where this code is internal.
// the culprit line
cell = row.insertCell(-1); cell.innerHTML = '<button class="calc" onclick="calculateGrade();"> + </button>';
Not sure if I need to post more code, but this line should show what I am attempting to do.
the button has to be done dynamically since it checks each row.
For educational purposes:
why is the button not able to respond?
is there a way to make this work as is?
is there another option to achieve the same results?
edit ----------------------------------
Just a snippit of the function it calls. code ommited as it never gets there
function calculateGrade() {
console.log('here'); // <--- THIS NEVER SHOWS UP, BUTTON IS NOT SENDING IT HERE
// code cleared for clarity
}
EDIT 2
for(let i=0; i < studentList.length-1; i++) {
row = table.insertRow(-1);
var
cell = row.insertCell(-1); cell.innerHTML = studentList[i];
cell = row.insertCell(-1); cell.innerHTML = grade
cell = row.insertCell(-1); cell.innerHTML = maxGrade;
cell = row.insertCell(-1); cell.innerHTML = student.name;
if(student.grade < maxGrade) { // change later to within 90%
cell = row.insertCell(-1); cell.innerHTML = '<button onclick="calculateGrade();" > + </button>';
}else{
cell = row.insertCell(-1); cell.innerHTML = 'Pass';
}
}

manipulation the dom
if you want to manipulate the dom "a hundrets of time" you should use a framework like jQuery or the javascript own functions for dom manipulation instead of innerHTML.
Why?
HTML has changed from time to time and you can't know, if your innerHTML will be correct (formatted) in the future.
document.createElementas example will work in every browser.
Also there is the possibility, that dom manipulation functions will be more optimized by compilers and engines in future.
Dynamic listeners
If you create dom elements dynamicly you don't have to declare the listeners as text.
let button = document.createElement('button');
button.addEventListener('onclick', calculateGrade);
cell.appendChild(button);
also this is less vulnerable for typos ;)
dynamic element creation and reflow
Whenever you change something in the dom that is a visible change, the browser fill fire a reflow. It will recalculate all elements sizes. As you can imagine, adding "a hundrets" of buttons will cause a hundrets of reflows.
Reflow will not trigger on invisible objects. so best practise if you manipulate a lot of elements is to turn the parent invisible, then make your changes and make the parent visible again. In this case you trigger a reflow only twice.

function myFunction() {
var row = document.getElementById("myRow");
var x = row.insertCell(-1);
x.innerHTML = '<button class="calc" onclick="calculateGrade();"> +button </button>';
}
function calculateGrade() {
alert("Hi am working action");
}
Check here the new add button is working when I am adding cell.innerHTML there will calc button and when you will click it will show "Hi am working action," I think it will help you
<!DOCTYPE html>
<html>
<head>
<style>
table, td {
border: 1px solid black;
}
</style>
</head>
<body>
<p>Click the button to insert new cell(s) at the beginning of the table row.</p>
<table>
<tr>
<td>First cell</td>
<td>Second cell</td>
<td>Third cell</td>
</tr>
<tr id="myRow">
<td>First cell</td>
<td>Second cell</td>
<td>Third cell</td>
</tr>
<tr>
<td>First cell</td>
<td>Second cell</td>
<td>Third cell</td>
</tr>
</table><br>
<button onclick="myFunction()">Try it</button>
</body>
</html>

Do not use onevent attributes:
<button onclick="func()">...</button>
Use either onevent property:
document.querySelector('button').onclick = function;
Or an eventListener:
document.querySelector('button').addEventListener('click', function)
Use Event Delegation -- a programming pattern that allows us to register a single event handler to listen for an unlimited number of elements. Here's a simplified outline:
Choose an element that is a common ancestor to all of the elements we want to react to an event. This includes window and document as well, but window should be used for key events (ex. document.onclick = calculateGrade)
The callback function (ex. calculateGrade(e)) must pass the Event Object (event, evt, or e are the most common names).
Reference the clicked, changed, hovered, etc. element with the event property Event.target (ex. const tgt = e.target).
Isolate Event.target by if...else control statements (ex. if (tgt.matches('button')) {...)
The rest of the code is just to dynamically generate a table for demonstration purposes. Although the code that generates a table in OP question is dubious it wasn't part of the question so there's no explanation for that part in the demo.
const tData = [{
name: 'zer00ne',
grade: 100
}, {
name: 'Trump',
grade: 25
}, {
name: 'Joe Average',
grade: 75
}, {
name: 'Slacker',
grade: 50
}];
const tHdrs = [`<th>#</th>`, `<th>Name</th>`, `<th>Grade</th>`, `<th> </th>`];
const frag = document.createDocumentFragment();
const table = document.createElement('table');
frag.appendChild(table);
const title = table.createCaption();
title.textContent = 'Student Grades';
const head = table.createTHead();
const hRow = head.insertRow(-1);
let h = tHdrs.length - 1;
for (let th of tHdrs) {
th = hRow.insertCell(0).outerHTML = tHdrs[h];
--h;
}
let rowQty = tData.length;
let colQty = tHdrs.length;
const body = document.createElement('tbody');
table.appendChild(body);
for (let r = 0; r < rowQty; r++) {
let row = body.insertRow(-1);
for (let c = 0; c < colQty; c++) {
let col = c => row.insertCell(c);
let cell = col(c);
if (c === 0) {
cell.textContent = r + 1;
} else if (c === colQty - 1) {
if (tData[r].grade > 69) {
cell.textContent = 'Pass';
} else {
cell.innerHTML = `<button>Calculate</button>`;
}
} else if (c === 1) {
cell.textContent = tData[r].name;
} else {
cell.textContent = tData[r].grade;
}
}
}
const foot = table.createTFoot();
const fRow = foot.insertRow(-1);
const fCol = fRow.insertCell(0);
fCol.textContent = 'Passing grade is 70% or more.'
fCol.setAttribute('colspan', '4');
fCol.className = 'note';
document.body.appendChild(frag);
// Relevant Code
document.onclick = calculateGrade;
function calculateGrade(e) {
const tgt = e.target;
if (tgt.matches('button')) {
let row = tgt.closest('tr');
let grd = row.children[2].textContent;
let num = parseFloat(grd);
console.log(num);
}
return false;
}
:root {
font: 400 5vh/1 Verdana;
}
table {
table-layout: fixed;
border-collapse: collapse;
width: 96%;
}
caption {
font-weight: 700;
font-size: 1.2rem;
}
th,
td {
border: 2px ridge #000;
min-height: 30px;
}
th:first-of-type {
width: 10%;
}
th:nth-of-type(3) {
width: 20%;
}
th:last-of-type {
width: 25%
}
td:first-of-type,
td:nth-of-type(3) {
text-align: center;
font-size: 1.2rem;
font-family: Consolas;
}
td:nth-of-type(3)::after {
content: '%';
}
td:first-of-type.note {
text-align: right;
font-size: 0.85rem;
font-family: Verdana;
border: none
}
button {
font: inherit;
width: 100%
}

Related

Looping through array of objects after button press

I'm trying to loop through array of objects to display them on a grid based off of input values for my library project. My code for loop is:
const addBook = (ev) => {
ev.preventDefault();
let myLibrary = [];
let bookInfo = {
title: document.getElementById('title').value,
author: document.getElementById('author').value,
pages: document.getElementById('pages').value,
}
myLibrary.push(bookInfo)
for (i = 0; i < myLibrary.length; i++) {
console.log(myLibrary)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
var submitBtn = document.querySelector('.submit-btn');
submitBtn.addEventListener('click', addBook)
Each time I enter title,author, and pages value and click submit button, it goes through and gives me a cell. If try to add another book, it gives me 3 cells rather then 2. Doing another add gives me 6 instead of 3. How can I make it where I can add a book each time one by one rather then it adding multiple times?
There are three main issues.
A missing curly brace after the function and before the button listener.
myLibrary is being redefined each time the function is called which is one of the reasons you're having to look over the data each time. You want to define it outside the function so you can add one book at a time to it when addBook is called.
With myLibrary no longer being redefined each time there's no need for the loop. We can just add the HTML for the book to the book shelf onSubmit.
(Note: In this working example (I've added some HTML for a table, and the inputs and buttons, and created some code to add the new book to the shelf), I've renamed the myLibrary variable to bookShelf to keep things consistent with the HTML naming.)
// Cache all the elements up front
const titleEl = document.getElementById('title');
const authorEl = document.getElementById('author');
const pagesEl = document.getElementById('pages');
const bookshelfEl = document.getElementById('bookshelf');
const submitBtn = document.querySelector('.submit-btn');
// Add the listener
submitBtn.addEventListener('click', addBook, false);
titleEl.focus();
// Our bookShelf variable is now
// outside the function
const bookShelf = [];
function addBook() {
// Because we've cached the elements
// we can now just grab the values from each
const bookInfo = {
title: titleEl.value,
author: authorEl.value,
pages: pagesEl.value,
}
bookShelf.push(bookInfo);
// Once we've added our book we can grab the
// title, author, and pages variables from it
const { title, author, pages } = bookInfo;
// Create a row for the table
const row = document.createElement('tr')
// Create some HTML and add it to the div
row.innerHTML = `
<td>${title}</td>
<td>${author}</td>
<td>${pages}</td>
`;
bookshelfEl.appendChild(row);
}
table { border-collapse: collapse; border: 2px solid #565656; width: 100%; }
td { text-align: center; }
.heading { background-color: #efefef; border-top: solid thin #565656; }
tr { border: solid thin #ababab; }
<input placeholder="Title" id="title" />
<input placeholder="Author" id="author" />
<input placeholder="Pages" id="pages" />
<button type="button" class="submit-btn">Submit</button>
<H3>Book shelf</H3>
<table>
<tbody id="bookshelf">
<tr class="heading">
<td>Title</td>
<td>Author</td>
<td>Pages</td>
</tr>
</tbody>
</table>
Additional documentation
Destructuring assignment
Template/string literals
Your issue is with the loop. Each time you click the submit button, you add a cell for each item in myLibrary in this block:
for (i = 0; i < myLibrary.length; i++) {
console.log(myLibrary)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
I don't think you need to loop through the array each time. Just add the cell directly whenever you are adding bookInfo data to myLibrary, without using for loop e.g
const addBook = (ev) => {
ev.preventDefault();
let myLibrary = [];
let bookInfo = {
title: document.getElementById('title').value,
author: document.getElementById('author').value,
pages: document.getElementById('pages').value,
}
myLibrary.push(bookInfo)
var container = document.getElementById('book-shelf')
var div = document.createElement('div')
div.classList.add('cell')
container.appendChild(div);
}
var submitBtn = document.querySelector('.submit-btn');
submitBtn.addEventListener('click', addBook)

QuerySelectorAll of a class does not include style properties?

I'm currently missing something about how querySelectorAll is working in my code. I'm using it to loop through and set event listeners of all tds in a table in order to grab reference to whichever one is clicked so that I can modify that td only. However in implementing a side functionality, I have run into a problem that I don't understand. It appears that even though each td has a class, and the class is given a background-color style via CSS, the value is considered blank unless I manually set it (via my paintColor function). Console.log reveals that the value is blank until its expressly set. Why?
Thanks
HTML
<table class="table">
<!-- Row 1 -->
<tr class="row">
<td class="cell"></td>
<td class="cell"></td>
<div class="menus">
<div class="menu-left">
<input type="checkbox" id="eye-check" unchecked/>
<span>Eyedropper</span>
<table class="eye-table">
<td class="eye-cell">
<span class="tooltip">Hexidecimal value of selected eyedropper
color.</span>
</td>
</table>
<div class="menu-left-text">
<p class="eye-text">rgb(255,255,255)</p>
</div>
</div>
CSS
.cell {
border: 1px solid lightgrey;
/* box-shadow: 1.5px 1.5px 1.5px lightgrey; */
background-color: #ffffff;
width: 16px;
height: 16px;
cursor: crosshair;
z-index: 4;
}
JS
// Gloabal Variables
let cells = document.querySelectorAll(".cell");
let activeColor = "rgb(255,255,255)";
let table = document.querySelector("table.table");
let activeCell = document.querySelector(".cell");
let dropper;
let eyeCell = document.querySelector(".eye-cell");
let eyeText = document.querySelector(".eye-text");
// Listen for EyeDropper
document.querySelector("#eye-check").addEventListener("click", evt =>{
dropper = document.querySelector("#eye-check");
if (dropper.checked) {
// Set Dropper Indicator on Table Border
table.style.border = "5px dashed black";
} else {
// Set Paint Indicator on Table Border
table.style.border = "5px solid silver";
}
});
// Listen for Clicks
for (let i = 0; i < cells.length; i++) {
cells[i].addEventListener("click", evt => {
activeCell = cells[i];
// Check for EyeDropper Toggle
checkDropper();
});
}
// Check for Eye Dropper Checked
function checkDropper() {
dropper = document.querySelector("#eye-check");
if (dropper.checked) {
activateDropper();
} else {
activatePaint();
}
}
// Dropper is Active, Activate Dropper
function activateDropper() {
// Set EyeDropper Color
eyeCell.style.backgroundColor = activeCell.style.backgroundColor;
// Set Text Value
eyeText.innerHTML = activeCell.style.backgroundColor.replace(/\s
/g,"");}
// Dropper is Inactive, Activate Painting
function activatePaint() {
// Get Active Color
getActiveColor();
// Paint Color into Cell
paintColor();
}
// Get Brush Color
function getActiveColor() {
activeColor = document.querySelector("#brush-color").value;
}
// Paint Color into active cell
function paintColor() {
activeCell.style.backgroundColor = activeColor;
}
Click here
I believe what is happening here is that the Node object doesn't have styles explicitly set on it and that's why you are getting the empty string when accessing the obj.style.whatever. HTML Node objects and CSS are separate entities and the CSS is telling the browser (not the Node) to change its style. If you set your CSS inline on the element, it should show when accessing it the way you do.
See here: http://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/style

How to change the value of an element using EventListener

In this program, I'm able to add inputs with a button but I need to show the length of each input as it changes. I'm able to get the length using an EventListener, but I'm not sure how to change the text value for any newly created buttons.
On line 12, you can see that I'm able to change the value successfully on the first input but I'm using an html variable. If you look at my addCell() function, you'll see that I have an element as a child of each node to keep track of the length of each input. I need to access that element in my change() function so I can set the event.target.value.length to the corresponding nodes child element.
I've tried using this, setting var x = this and I've tried using the event.target properties to find the corresponding node and even innerHTML.
var i = 0;
var count = 1;
var length = 2;
var chars = 0;
document.addEventListener('input', function (evt) {
change(evt);
});
function change(elem) {
var check = document.getElementById("first");
if (event.target == check) {
document.getElementById("len").innerHTML = event.target.value.length;
return;
}
// Here's where I'm stuck
}
function removeCell() {
if (count <= 1) {
alert("Illegal operation, the police have been notified.")
return;
}
var elem = document.getElementById('main');
elem.removeChild(elem.lastChild);
count = count - 1;
length = length - 1;
}
function addCell() {
var node = document.createElement('div');
node.innerHTML += length;
var inp = document.createElement('INPUT');
var size = document.createElement('size');
inp.setAttribute("type", "text");
node.appendChild(inp);
node.appendChild(size);
document.getElementById('main').appendChild(node);
count += 1;
length += 1;
i += 1;
}
#area {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
#texts {
width: 220px;
height: 50px;
border-style: solid;
}
body {
background-color: grey;
}
<div id="area">
<form id="main">
<pre><b> input </b> length</pre>
<span id="list">
1<input type="text" id="first"> <var id="len"></var>
</span>
</form>
<br />
<button onclick="addCell()">Add Cell</button>
<button onclick="removeCell()">Remove Cell</button>
<button onclick="sort()">Sort</button>
</div>
Since I'm able to use alert() to show me the correct length of each newly created input each time it changes, I know there's a way to access the "size" element I created to update it using event.target.value.length
Your problem is that you use a "global" input event listener and your change() function is not programmed to handle multiple input fields because in it you are querying known element ids first and len.
If you want to go with a global listener you have to tell your change() function how to access the new input and corresponding target fields.
An easier way is that you modify your addCell() function and attach an event listener to the input field that you are creating instead of using a global one. Thereby each input field holds its own event listener. Since both the input field and your size element, which displays the length of the input value, are created in the same scope you can use easily write the length to the corresponding size element.
inp.addEventListener('input', function(){
size.innerText = inp.value.length;
});
If you want this to work with your provided HTML you need to remove your first input field and call addCell() manually so that your initial input gets rendered.
Your code should then look like this (note: I set var count = 0; and var length = 1;):
var i = 0;
var count = 0;
var length = 1;
var chars = 0;
function removeCell() {
if (count <= 1) {
alert("Illegal operation, the police have been notified.")
return;
}
var elem = document.getElementById('main');
elem.removeChild(elem.lastChild);
count = count - 1;
length = length - 1;
}
function addCell() {
var node = document.createElement('div');
node.innerHTML += length;
var inp = document.createElement('INPUT');
var size = document.createElement('size');
inp.setAttribute("type", "text");
inp.addEventListener('input', function(){
size.innerText = inp.value.length;
});
node.appendChild(inp);
node.appendChild(size);
document.getElementById('main').appendChild(node);
count += 1;
length += 1;
i += 1;
}
addCell();
#area {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
#texts {
width: 220px;
height: 50px;
border-style: solid;
}
body {
background-color: grey;
}
<div id="area">
<form id="main">
<pre><b> input </b> length</pre>
<span id="list"></span>
</form>
<br />
<button onclick="addCell()">Add Cell</button>
<button onclick="removeCell()">Remove Cell</button>
<button onclick="sort()">Sort</button>
</div>
If HTML layout is planned out and is consistent you can use [name] attribute for form controls and .class or even just the tagName. Use of #id when dealing with multiple tags is difficult and unnecessary. Just in case if you weren't aware of this critical rule: #ids must be unique there cannot be any duplicate #ids on the same page. Having duplicate #ids will break JavaScript/jQuery 90% of the time.
To accessing tags by .class, #id, [name], tagName, etc. use document.querySelector() and document.querySelectorAll() for multiple tags.
To access forms and form controls (input, output, select, etc) by [name] or #id use the HTMLFormElement and HTMLFormControlsCollection APIs.
.innerHTML is destructive as it overwrites everything within a tag. .insertAdjacentHTML() is non-destructive and can place an htmlString in 4 different positions in or around a tag.
Event handlers and event listeners work only on tags that were initially on the page as it was loaded. Any tags dynamically added afterwards cannot be registered to listen/handle events. You must delegate events by registering an ancestor tag that's been on the page since it was loaded. This was done with delRow() since the buttons are dynamically created on each row (changed it because one delete button that removes the last row isn't that useful. ex. 7 rows and you need to delete 4 rows just to get to the third row).
Here's a breakdown of: [...ui.len] ui references all form controls .len is all tags with the [name=len]. The brackets and spread operator converts the collection of len tags to an array.
There's no such thing as <size></size>. So document.createElement('size') is very wrong.
const main = document.forms.main;
main.oninput = count;
main.elements.add.onclick = addRow;
document.querySelector('tbody').onclick = delRow;
function count(e) {
const active = e.target;
const ui = e.currentTarget.elements;
const row = active.closest('tr');
const idx = [...row.parentElement.children].indexOf(row);
const length = [...ui.len][idx];
length.value = active.value.length;
return false;
}
function addRow(e) {
const tbody = document.querySelector('tbody');
let last = tbody.childElementCount+1;
tbody.insertAdjacentHTML('beforeend', `<tr><td data-idx='${last}'><input name='txt' type="text"></td><td><output name='len'>0</output></td><td><button class='del' type='button'>Delete</button></td>`);
return false;
}
function delRow(e) {
if (e.target.matches('.del')) {
const row = e.target.closest('tr');
let rows = [...row.parentElement.children];
let qty = rows.length;
let idx = rows.indexOf(row);
for (let i = idx; i < qty; i++) {
rows[i].querySelector('td').dataset.idx = i;
}
row.remove();
}
return false;
}
body {
background-color: grey;
}
#main {
width: 585px;
background-color: lightgrey;
color: black;
border-style: solid;
margin-left: auto;
margin-right: auto;
min-height: 100px;
height: auto
}
tbody tr td:first-of-type::before {
content: attr(data-idx)' ';
}
<form id="main">
<table>
<thead>
<tr>
<th class='txt'>input</th>
<th class='len'>length</th>
<th><button id='add' type='button'>Add</button></th>
</tr>
</thead>
<tbody>
<tr>
<td data-idx='1'><input name='txt' type="text"></td>
<td><output name='len'>0</output></td>
<td><button class='del' type='button'>Delete</button></td>
</tr>
</tbody>
</table>
<!--These are dummy nodes because of the
HTMLFormControlsCollection API ability to use id or name, there
must be at least 2 tags with the same name in order for it to
be considered iterable-->
<input name='txt' type='hidden'>
<input name='len' type='hidden'>
</form>

Add id dynamically to each table cells

I am trying to create a dynamic js table and I want to give id to each cell dynamically. I want to use those ids to use in different js event handlers. How it can be done? I have tried in different ways but none of them works!
<html>
<head>
<style>
#colors {
float: right;
width: 400px;
height: 400px;
}
</style>
</head>
<body>
<script>
var d;
var k = 0;
function makeit() {
var tbl = document.createElement("table");
var atts = document.createAttribute("style");
atts.value = "border:1px solid black;text-align:center;padding:2px;margin:3px 3px 3px 3px;";
tbl.setAttributeNode(atts);
for (i = 0; i < 5; i++) {
var rows = tbl.insertRow(i);
for (j = 0; j < 7; j++) {
d = rows.insertCell(j);
d.height = "50px";
d.width = "50px";
d.style.backgroundColor = "yellow";
d.addEventListener("click", function myfunc() { d.style.backgroundColor = "red"; });
}
}
document.body.appendChild(tbl);
}
window.onload = makeit;
</script>
</body>
</html>
Just add
d.id = "r" + i + "c" + j;
under
d=rows.insertCell(j);
to set unique ids on each td.
Obviously, you can change the syntax r2c4 (which would be 3. row and the 5. cell) to your own liking.
If you want to call a function when clicking on a specific td you could even pass the row index (i) and column index (j) to that function.
Side note
You should consider using a JavaScript library or framework like jQuery for manipulations like this. It would facilitate your work a lot in the long term.
The problem is a scope issue. When adding the event listener, d's reference gets updated to be the last table cell you have created.
You can simply change the event listener's function to:
function myfunc() {
this.style.backgroundColor="red";
}
So that this references the object it is attached to. Depending on your intention, you may not need unique ids if you have access to the cell itself.
Using an approach that includes wongcode's solution, you may wish to consider the following code:
<html>
<head>
<style>
#myTbl{ border:1px solid black;text-align:center;padding:2px;margin:3px 3px 3px 3px; }
#myTbl td{ width: 50px; height: 50px; background-color: yellow;}
</style>
</head>
<body>
<script>
function onCellClicked(e)
{
this.style.backgroundColor = 'red';
}
function makeit()
{
var tbl=document.createElement("table");
tbl.id = 'myTbl';
var curCellIndex = 0;
for(i=0;i<5;i++)
{
var rows=tbl.insertRow(i);
for(j=0;j<7;j++)
{
d=rows.insertCell(j);
d.id = 'cell_' + curCellIndex;
curCellIndex++;
d.addEventListener("click",onCellClicked, false);
}
}
document.body.appendChild(tbl);
}
window.onload=makeit;
</script>
</body>
</html>
Some of the advantages include:
Smaller html file created in your editor
Smaller html code created in the browser
Use of context and the this keyword
Smaller memory consumption, since each TD doesn't contain the full
body of the event handler (it only include a 'pointer' to the
function to be executed)
EDIT: forgot to add code to give the cells an id. Now fixed.

Change background color of cell when value = "Late"

I have this piece of code:
<style>
[contenteditable]:hover, [contenteditable]:focus {
background: #D7D7FF;
}
</style>
To have the background of a cell from a table change colors when hovered or when focused.
Now one of the columns' name is Status. If the Status is late, meaning the value of that cell is = "Late", I would like the background to change to #FFD7D7. How would I write this in javascript?
Since this <style> is at the beginning of the html file and not inside a CSS I'm a little lost and don't know how to do this.
Any help is greatly appreciated!
If you didn't want to use jQuery, here's the code in JavaScript
http://jsfiddle.net/u72yF/3/
color = function() {
var table = document.getElementById('myTable');
var trList = table.getElementsByTagName('tr');
// index of the status column
var index = -1;
for (var i = 0; i < trList.length; i++) {
var tdList = trList[i].getElementsByTagName('td');
if (index < 0) {
// we need to find out the status column index first
for (var j = 0; j < tdList.length; j++) {
if (tdList[j].id == 'statusColumn') {
index = j;
break;
}
}
}
if (tdList[index].innerHTML.trim() == 'Late') {
// for constant coloring
trList[i].style.background = '#FFD7D7';
// for on hover coloring
//trList[i].onmouseover = function() {
// this.style.background = '#FFD7D7';
//}
//trList[i].onmouseout = function() {
// this.style.background = '';
//}
}
}
}
I assumed, the code does not know, which column is a Status one, so there is a detection included (by id "statusColumn"). The "Late" string is then searched only in this column, other ones are ignored.
If by coloring you meant on hover coloring rather than constant coloring (which I demonstrated), remove the comment in the bottom part, which implements this.
One possibility follows. IMO, this could be expressed much more clearly using jQuery, but since you didn't mention it we'll ride JavaScript bareback on this one:
<!DOCTYPE html>
<html>
<head>
<style>
[contenteditable]:hover, [contenteditable]:focus {
background: #D7D7FF;
}
.late {
background-color: #FFD7D7;
}
</style>
<script type="text/javascript">
function checkStatus(ev) {
var statusField = document.getElementById("status-field");
if (statusField.textContent == 'Late') {
statusField.className += " late";
} else {
statusField.className = statusField.className.replace(/(?:^|\s)late(?!\S)/g , '');
}
return true;
}
</script>
</head>
<body>
<table>
<tr>
<th>Name</th>
<th>Status</th>
</tr>
<tr>
<td contenteditable="true">Juan Carlos</td>
<td id="status-field" contenteditable="true" onblur="return checkStatus()">Early</td>
</tr>
</table>
</body>
</html>
$('.status').on('blur keyup paste', function() {
$this = $(this);
$(this).css('background', ($this.text() == 'Late') ? 'red' : 'inherit');
});
where every status cell has status class

Categories

Resources