facebook google plus twitter
Webucator's Free JavaScript Tutorial

Lesson: CSS Object Model

Welcome to our free JavaScript tutorial. This tutorial is based on Webucator's Introduction to JavaScript Training course.

We can use JavaScript to both retrieve information about element's CSS styles and to set those styles programmatically.

Lesson Goals

  • Learn to change the values of CSS properties dynamically.
  • Learn to hide and show elements.
  • Learn to dynamically modify the content of elements.
  • Learn to manipulate tables dynamically.
  • Learn to position elements dynamically.
  • Learn to change the z-index order of elements.

Changing CSS with JavaScript

Throughout this course we've changed the style of a DOM element with JavaScript - setting the background color of the page, say, or altering the text color of an element. Typically, we use the following syntax:

element.style.cssproperty = 'cssvalue';

where element might be a DOM element gotten from its id (via getElementById()), cssproperty something like color (to set the text color), and cssvalue something like red. We can both get and set any styles for most any element.

Each CSS property has a corresponding property of the JavaScript style object:

  • If the CSS property is a simple word (e.g., color) then the JavaScript property is the same (e.g., style.color).
  • If the CSS property has a dash in it (e.g., background-color) then the JavaScript property uses lower camel case (e.g., style.backgroundColor).

The style object is a collection of an element's styles that are either defined within that HTML element's style attribute or directly in JavaScript. Styles defined in the <style> tag or in an external style sheet are not part of the style object.

The W3C specifies a method for getting at the current (or actual) style of an object: the window object's getComputedStyle() method.

window.getComputedStyle(Element)

Note that the reference to window can be excluded as window is the implicit object. For example:

var div = document.getElementById("divTitle");
var computedStyle = getComputedStyle(div);
alert(computedStyle.fontWeight);
var curStyle = getComputedStyle(div);
alert(curStyle.fontWeight);

Using this method - with getComputedStyle(), as opposed to element.style - we can get for any element the styles set with inline CSS, from CSS in the head of the page, or in an external stylesheet. Furthermore, as the name of the method getComputedStyle() suggests, these are computed (calculated) styles: whereas element.style just gives us style info as set in CSS, getComputedStyle() gives us the real-time calculated CSS.

Let's take a look at a simple example to make this more clear.

Code Sample:

CSSObjectModel/Demos/styles.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Styles</title>
<link href="customstyles.css" rel="stylesheet">
<script>
	window.onload = function() {
		document.getElementById("getstyles").addEventListener("click", function() {
			var pgraph = document.getElementById("pgraph");
			var styleString = "Styles\n";
			styleString += 'color: ' + pgraph.style.color + "\n";
			styleString += 'margin: ' + pgraph.style.margin + "\n";
			styleString += 'padding: ' + pgraph.style.padding + "\n";
			styleString += 'border: ' + pgraph.style.borderBottom + "\n";
			styleString += 'width: ' + pgraph.style.width + "\n";
			alert(styleString);

			            var computedStyle = getComputedStyle(pgraph);
			var computedStyleString = "Computed Styles\n";
			computedStyleString += 'color: ' + computedStyle.color + "\n";
			computedStyleString += 'margin: ' + computedStyle.margin + "\n";
			computedStyleString += 'padding: ' + computedStyle.padding + "\n";
			computedStyleString += 'border: ' + computedStyle.borderBottom + "\n";
			computedStyleString += 'width: ' + computedStyle.width + "\n";
			alert(computedStyleString);
			return false;
		});
		document.getElementById("setstyles").addEventListener("click", function() {
			var pgraph = document.getElementById("pgraph");
			pgraph.style.borderBottom = 'none';
			pgraph.style.fontStyle = 'normal';
			pgraph.style.width = 'auto';
			pgraph.style.padding = 'auto';
			pgraph.style.margin = 'auto';
			return false;
		});
	}
</script>
<style>
	#pgraph {
		border-bottom:2px solid blue;
		width:80%;
		padding:10px;
	}
</style>
</head>
<body>
<h1>Styles</h1>
<p id="pgraph" style="margin:10px auto 30px auto">This is a paragraph</p>
<button id="getstyles">Get Styles</button>
<button id="setstyles">Set Styles</button>
</body>
</html>

Code Explanation

The paragraph, with id pgraph, has CSS styles set in three different ways: inline (margin), in the head of the page (border-bottom, width, and padding), and from an external stylesheet customstyles.css (font-size, color, and font-style).

Clicking the "Get Styles" button invokes a handler which pops up two JavaScript alerts. The first lists the styles retrieved pgraph.style; note that color, padding, border, and width are all blank. The second alert, however, displays style information retrieved from getComputedStyle(pgraph); note that all of the style information - regardless of whether it was added inline, in the head of the page, or from the external stylesheet, is displayed.

The "Set Styles" button, when clicked, changes the styles of the paragraph. Check out how the two methods of getting the styles differ when you click "Get Styles" after setting the styles.

Hiding and Showing Elements

Elements can be hidden and shown by changing their visibility or display values. The visibility style can be set to visible or hidden and the display property can be set to block, table-row, none, and several other values. The two work slightly differently as the following example illustrates:

Code Sample:

CSSObjectModel/Demos/Visibility.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Showing and Hiding Elements with JavaScript</title>
<link href="visibility.css" rel="stylesheet">
<script>
function changeVisibility(id){
	var elem = document.getElementById(id);
	var visibility;
	if (elem.style.visibility == "hidden") {
		elem.style.visibility = "visible";
	} else {
		elem.style.visibility = "hidden";
	}
	var	computedStyle = getComputedStyle(elem,null);
	visibility = computedStyle["visibility"];
	msg("The <em>visibility</em> of row <em>" + id + "</em> is <em>" + visibility + "</em>.");
}

function changeDisplay(id){
	var elem = document.getElementById(id);
	var display;
	if (elem.style.display == "none") {
		elem.style.display = "";
	} else {
		elem.style.display = "none";
	}
	var	computedStyle = getComputedStyle(elem,null);
	display = computedStyle["display"];
	msg("The <em>display</em> of row <em>" + id + "</em> is <em>" + display + "</em>.");
}

function msg(text) {
	document.getElementById("msg").innerHTML = text;
}
</script>
</head>
<body>
<h1>Hiding and Showing Elements</h1>
<table>
	<tr id="tr1"><td>Row 1</td></tr>
	<tr id="tr2"><td>Row 2</td></tr>
	<tr id="tr3"><td>Row 3</td></tr>
	<tr id="tr4"><td>Row 4</td></tr>
</table>
<div id="msg">Style messages</div>
<h2>visibility</h2>
<button onclick="changeVisibility('tr1')">Row 1</button>
<button onclick="changeVisibility('tr2')">Row 2</button>
<button onclick="changeVisibility('tr3')">Row 3</button>
<button onclick="changeVisibility('tr4')">Row 4</button>

<h2>display</h2>
<button onclick="changeDisplay('tr1')">Row 1</button>
<button onclick="changeDisplay('tr2')">Row 2</button>
<button onclick="changeDisplay('tr3')">Row 3</button>
<button onclick="changeDisplay('tr4')">Row 4</button>
</body>
</html>

Code Explanation

This page has two functions: changeVisibility() and changeDisplay(). The changeVisibility() function checks the value of the visibility style of the passed element and changes it to its opposite. The changeDisplay() function does the same with the display style. The functions are called with buttons on the page and are passed in ids of table rows from the table on the page.

The main difference between setting visibility to hidden and setting display to none is that setting visibility to hidden does not modify the layout of the page; it simply hides the element. Setting display to none, on the other hand, collapses the element, so that the surrounding relatively positioned elements re-position themselves.

Check out the screenshot below:Hide Show

Row 1 has visibility set to hidden, so you can see the space for the row, but you cannot see the row itself. Row 3 has display set to none so it's as if the row was not even there.

Showing and Hiding Elements

Duration: 20 to 30 minutes.

In this exercise, you will modify a Math Quiz to only show the countdown timer when it is running.

  1. Open CSSObjectModel/Exercises/MathQuiz.html for editing.
  2. Modify the code so that the table row with the timer in it (the last row) only shows up when the timer is running.Math Quiz Math Quiz Hint: You will make your changes in resetTimer() and questionChanged().
  3. Test your solution in a browser.

Challenge

Only show the Answer row when a question is selected.

Solution:

CSSObjectModel/Solutions/MathQuiz.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Math Quiz</title>
<link href="MathQuiz.css" rel="stylesheet">
<script src="questions.js"></script>
<script>	
	var timer, timePerQuestion = 10;
	window.onload = function() {
		init();
	}
	
	function init() {
		var category = document.getElementById("category");
		var question = document.getElementById("question");
		var btnCheck = document.getElementById("btnCheck");
		var timeLeft = document.getElementById("timeLeft");
		category.addEventListener('change', function() {
			selChanged(category,categories,question);
			resetTimer(timePerQuestion);
		});
		question.addEventListener('change', function() {
			questionChanged();
		});
		btnCheck.addEventListener('click', function() {
			checkAnswer();
		});
		timeLeft.addEventListener('focus', function() {
			timeLeft.blur();
		});
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}

	function selChanged(sel,data,dependentSel) {
		var selection = sel.options[sel.selectedIndex].value;
		var arrOptions = data[selection];
		var opt;
		dependentSel.options.length = 1;
		for (var i in arrOptions) {
			opt = new Option(arrOptions[i].q,arrOptions[i].a);
			dependentSel.add(opt);
		}
	}
	
	function resetTimer(seconds) {
		var timerRow = document.getElementById("timerRow");
		timerRow.style.display = "none";
		document.getElementById("timeLeft").value = seconds;
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var timeLeft = document.getElementById("timeLeft");
		timeLeft.value--;
		if (timeLeft.value < 0) {
			resetTimer(timePerQuestion);
			msg("Time's up!  The answer is " + getAnswer() + ".");
			removeOption();
		}
	}

	function checkAnswer() {
		var userAnswer = document.getElementById("answer").value;
		var correctAnswer = getAnswer();
		
		if (userAnswer === correctAnswer) {
			msg("Right! The answer is " + correctAnswer + ".");
		} else {
			msg("Sorry. The correct answer is " + correctAnswer + ".");
		}
		removeOption();
		questionChanged();
	}
	
	function getAnswer() {
		var i = document.Quiz.question.selectedIndex;
		var answer = document.Quiz.question[i].value;
		return answer;
	}
	
	function removeOption() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		question.remove(question.selectedIndex);
		if(question.options.length == 1) {
			category.remove(category.selectedIndex);
			if (category.options.length == 0) {
				endQuiz();
			} else {
				selChanged(category,categories,question);
			}
		}
		resetTimer(timePerQuestion);
	}
	
	function questionChanged() {
		document.Quiz.answer.value="";
		var timerRow;
		if (document.Quiz.question.selectedIndex === 0) {
			document.Quiz.btnCheck.disabled = true;
			document.Quiz.answer.disabled = true;
			resetTimer(timePerQuestion);
		} else {
			document.Quiz.btnCheck.disabled = false;
			document.Quiz.answer.disabled = false;
			document.Quiz.answer.focus();
			timer = setInterval(decrementTimer,1000);
			timerRow = document.getElementById("timerRow");
			timerRow.style.display = "";
		}
	}
	
	function endQuiz() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		resetTimer(timePerQuestion);
		alert("Thanks for playing! The quiz will now reload.");
		category.add(new Option("Addition","addition"));
		category.add(new Option("Subtraction","subtraction"));
		category.add(new Option("Multiplication","multiplication"));
		category.add(new Option("Division","division"));
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}
	
	function msg(text) {
		document.getElementById("msg").innerHTML = text;	
	}
</script>
</head>
<body>
<form name="Quiz" onsubmit="return false;">
<table>
<tr>
	<td>Category:</td>
	<td>
		<select name="category" id="category">
			<option value="addition">Addition</option>
			<option value="subtraction">Subtraction</option>
			<option value="multiplication">Multiplication</option>
			<option value="division">Division</option>
		</select>
	</td>
</tr>
<tr>
	<td>Question:</td>
	<td>
		<select name="question" id="question">
			<option value="0">--Please Choose--</option>
		</select>
	</td>
</tr>
<tr>
	<td>Answer:</td>
	<td>
		<input type="text" name="answer" id="answer" size="2" disabled="disabled">
		<input type="button" name="btnCheck" id="btnCheck" value="Check Answer" disabled="disabled">
	</td>
</tr>
<tr id="timerRow">
	<td>Timer:</td>
	<td><input type="text" name="timeLeft" id="timeLeft" size="2"> seconds left</td>
</tr>
<tr>
	<td id="msg" colspan="2">Good luck!</td>
</tr>
</table>
</form>
</body>
</html>

Challenge Solution:

CSSObjectModel/Solutions/MathQuiz-challenge.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Math Quiz</title>
<link href="MathQuiz.css" rel="stylesheet">
<script src="questions.js"></script>
<script>	
	var timer, timePerQuestion=10;
	window.onload = function() {
		init();
	}
	
	function init() {
		var category = document.getElementById("category");
		var question = document.getElementById("question");
		var btnCheck = document.getElementById("btnCheck");
		var timeLeft = document.getElementById("timeLeft");
		category.addEventListener('change', function() {
			selChanged(category,categories,question);
			resetTimer(timePerQuestion);
		});
		question.addEventListener('change', function() {
			questionChanged();
		});
		btnCheck.addEventListener('click', function() {
			checkAnswer();
		});
		timeLeft.addEventListener('focus', function() {
			timeLeft.blur();
		});
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}

	function selChanged(sel,data,dependentSel) {
		var selection = sel.options[sel.selectedIndex].value;
		var arrOptions = data[selection];
		var opt;
		dependentSel.options.length = 1;
		for (var i in arrOptions) {
			opt = new Option(arrOptions[i].q,arrOptions[i].a);
			dependentSel.add(opt);
		}
	}
	
	function resetTimer(seconds) {
		var timerRow = document.getElementById("timerRow");
		var answerRow = document.getElementById("answerRow");
		timerRow.style.display = "none";
		answerRow.style.display = "none";
		document.getElementById("timeLeft").value = seconds;
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var timeLeft = document.getElementById("timeLeft");
		timeLeft.value--;
		if (timeLeft.value < 0) {
			resetTimer(timePerQuestion);
			msg("Time's up!  The answer is " + getAnswer() + ".");
			removeOption();
		}
	}

	function checkAnswer() {
		var userAnswer = document.getElementById("answer").value;
		var correctAnswer = getAnswer();
		
		if (userAnswer === correctAnswer) {
			msg("Right! The answer is " + correctAnswer + ".");
		} else {
			msg("Sorry. The correct answer is " + correctAnswer + ".");
		}
		removeOption();
		questionChanged();
	}
	
	function getAnswer() {
		var i = document.Quiz.question.selectedIndex;
		var answer = document.Quiz.question[i].value;
		return answer;
	}
	
	function removeOption() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		question.remove(question.selectedIndex);
		if(question.options.length == 1) {
			category.remove(category.selectedIndex);
			if (category.options.length == 0) {
				endQuiz();
			} else {
				selChanged(category,categories,question);
			}
		}
		resetTimer(timePerQuestion);
	}
	
	function questionChanged() {
		document.Quiz.answer.value="";
		var timerRow,answerRow;
		if (document.Quiz.question.selectedIndex === 0) {
			document.Quiz.btnCheck.disabled = true;
			document.Quiz.answer.disabled = true;
			resetTimer(timePerQuestion);
		} else {
			document.Quiz.btnCheck.disabled = false;
			document.Quiz.answer.disabled = false;
			timer = setInterval(decrementTimer,1000);
			timerRow = document.getElementById("timerRow");
			timerRow.style.display = "";
			answerRow = document.getElementById("answerRow");
			answerRow.style.display = "";
			document.Quiz.answer.focus();
		}
	}
	
	function endQuiz() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		resetTimer(timePerQuestion);
		alert("Thanks for playing! The quiz will now reload.");
		category.add(new Option("Addition","addition"));
		category.add(new Option("Subtraction","subtraction"));
		category.add(new Option("Multiplication","multiplication"));
		category.add(new Option("Division","division"));
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}
	
	function msg(text) {
		document.getElementById("msg").innerHTML = text;	
	}
</script>
</head>
<body>
<form name="Quiz" onsubmit="return false;">
<table>
<tr>
	<td>Category:</td>
	<td>
		<select name="category" id="category">
			<option value="addition">Addition</option>
			<option value="subtraction">Subtraction</option>
			<option value="multiplication">Multiplication</option>
			<option value="division">Division</option>
		</select>
	</td>
</tr>
<tr>
	<td>Question:</td>
	<td>
		<select name="question" id="question">
			<option value="0">--Please Choose--</option>
		</select>
	</td>
</tr>
<tr id="answerRow">
	<td>Answer:</td>
	<td>
		<input type="text" name="answer" id="answer" 
			size="2" disabled="disabled">
		<input type="button" name="btnCheck" id="btnCheck" 
			value="Check Answer" disabled="disabled">
	</td>
</tr>
<tr id="timerRow">
	<td>Timer:</td>
	<td><input type="text" name="timeLeft" id="timeLeft" size="2"> seconds left</td>
</tr>
<tr>
	<td id="msg" colspan="2">Good luck!</td>
</tr>
</table>
</form>
</body>
</html>

Manipulating Tables

HTML tables can be created and manipulated dynamically with JavaScript. Each table element contains a rows array and methods for inserting and deleting rows: insertRow() and deleteRow(). Each tr element contains a cells array and methods for inserting and deleting cells: insertCell() and deleteCell(). The following example shows how these objects can be used to dynamically create HTML tables:

Code Sample:

CSSObjectModel/Demos/Table.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Manipulating Tables</title>
<link href="table.css" rel="stylesheet">
<script>
function addRow(tableId, cells){
	var tableElem = document.getElementById(tableId);
	var newRow = tableElem.insertRow(tableElem.rows.length);
	var newCell;
	for (var i=0; i < cells.length; i++) {
		newCell = newRow.insertCell(newRow.cells.length);
		newCell.innerHTML = cells[i];
	}
	return newRow;
}

function deleteRow(tableId, rowNumber){
	var tableElem = document.getElementById(tableId);
	if (rowNumber > 0 && rowNumber < tableElem.rows.length) {
		tableElem.deleteRow(rowNumber);
	} else {
		alert("Failed");
	}
}

window.onload = function() {
	var btnAdd = document.getElementById("btnAdd");
	var btnDelete = document.getElementById("btnDelete");
	btnAdd.addEventListener('click', function() {
		var cells = [btnAdd.form.FirstName.value,btnAdd.form.LastName.value];
		addRow('tblPeople', cells);
	});
	btnDelete.addEventListener('click', function() {
		deleteRow('tblPeople', btnDelete.form.RowNum.value)
	});
};
</script>
</head>
<body>
<table id="tblPeople">
<tr>
	<th>First Name</th>
	<th>Last Name</th>
</tr>
</table>
<hr>
<form name="formName">
	<label for="FirstName">First Name:</label>
	<input type="text" name="FirstName"><br>
	<label for="LastName">Last Name:</label>
	<input type="text" name="LastName"><br>
	<input type="button" id="btnAdd" value="Add Name">
	<hr>
	<label for="RowNum">Remove Row:</label>
	<input type="text" size="1" name="RowNum">
	<input type="button" id="btnDelete" value="Delete Row">
</form>
</body>
</html>

Code Explanation

The body of the page contains a table with an id of tblPeople. The table contains a single row of headers.

<table id="tblPeople"> <tr> <th>First Name</th> <th>Last Name</th> </tr> </table>

Below the table is a form that allows the user to enter a first and last name. When the "Add Name" button is clicked, the addRow() function is called and passed in the id of the table (tblPeople) and a new array containing the user-entered values:

var cells = [btnAdd.form.FirstName.value, btnAdd.form.LastName.value]; addRow('tblPeople', cells);

The addRow() function uses the insertRow() method of the table to add a new row at the end of the table and then loops through the passed-in array, creating and populating one cell for each item. The function also returns the new row. Although the returned value isn't used in this example, it can be useful if you then want to manipulate the new row further.

function addRow(tableId, cells){ var tableElem = document.getElementById(tableId); var newRow = tableElem.insertRow(tableElem.rows.length); var newCell; for (var i=0; i < cells.length; i++) { newCell = newRow.insertCell(newRow.cells.length); newCell.innerHTML = cells[i]; } return newRow; }

The form also contains a "Delete Row" button that, when clicked, passes the id of the table (tblPeople) and the number entered by the user in the RowNum text field.

deleteRow('tblPeople', btnDelete.form.RowNum.value)

The deleteRow() function checks to see if the row specified exists and is not the first row (row 0, which is the header row). If both conditions are true, it deletes the row. Otherwise, it alerts "Failed".

function deleteRow(tableId, rowNumber){ var tableElem = document.getElementById(tableId); if (rowNumber > 0 && rowNumber < tableElem.rows.length) { tableElem.deleteRow(rowNumber); } else { alert("Failed"); } }

Tracking Results in the Math Quiz

Duration: 15 to 25 minutes.

In this exercise, you will dynamically create a table that shows the user how she is doing on the Math Quiz. The screenshot below shows how the result will look:Quiz Result

  1. Open CSSObjectModel/Exercises/MathQuizTable.html for editing.
  2. Change the msg() function so that it takes a second argument: color, and uses it to change the text of the "msg" div to the passed-in color.
  3. Change all calls to msg() to pass in a color as well as the text: green if the answer is correct and red if it is not.
  4. Option objects have a text property that holds the displayed text of the option. Add a getQuestion() function that returns the text of the selected question.
  5. Notice that there is a table at the bottom of the body of the page with an id of tblResults.
  6. Modify the checkAnswer() function so that it adds a new row to the results table showing the question, user's answer, and correct answer.
  7. Test your solution in a browser.

Challenge

Modify the code so that the new row's background color is #00ff00 if the answer is correct and #ff9999 if it is not. Hint: the addRow() function returns the newly added row.

Solution:

CSSObjectModel/Solutions/MathQuizTable.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Math Quiz</title>
<link href="MathQuiz.css" rel="stylesheet">
<script src="questions.js"></script>
<script>	
	var timer, timePerQuestion = 10;
	window.onload = function() {
		init();
	}
	
	function init() {
		var category = document.getElementById("category");
		var question = document.getElementById("question");
		var btnCheck = document.getElementById("btnCheck");
		var timeLeft = document.getElementById("timeLeft");
		category.addEventListener('change', function() {
			selChanged(category,categories,question);
			resetTimer(timePerQuestion);
		});
		question.addEventListener('change', function() {
			questionChanged();
		});
		btnCheck.addEventListener('click', function() {
			checkAnswer();
		});
		timeLeft.addEventListener('focus', function() {
			timeLeft.blur();
		});
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}

	function selChanged(sel,data,dependentSel) {
		var selection = sel.options[sel.selectedIndex].value;
		var arrOptions = data[selection];
		var opt;
		dependentSel.options.length = 1;
		for (var i in arrOptions) {
			opt = new Option(arrOptions[i].q,arrOptions[i].a);
			dependentSel.add(opt);
		}
	}
	
	function resetTimer(seconds) {
		var timerRow = document.getElementById("timerRow");
		var answerRow = document.getElementById("answerRow");
		timerRow.style.display = "none";
		answerRow.style.display = "none";
		document.getElementById("timeLeft").value = seconds;
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var timeLeft = document.getElementById("timeLeft");
		timeLeft.value--;
		if (timeLeft.value < 0) {
			resetTimer(timePerQuestion);
			msg("Time's up!  The answer is " + getAnswer() + ".","red");
			removeOption();
		}
	}


	function addRow(tableId, cells) {
		var tableElem = document.getElementById(tableId);
		var newRow = tableElem.insertRow(tableElem.rows.length);
		var newCell;
		for (var i = 0; i < cells.length; i++) {
			newCell = newRow.insertCell(newRow.cells.length);
			newCell.innerHTML = cells[i];
		}
		return newRow;
	}

	function checkAnswer() {
		var userAnswer = document.getElementById("answer").value;
		var correctAnswer = getAnswer();
		var question = getQuestion();
		var arrCells = [question, userAnswer, correctAnswer];
		addRow("tblResults", arrCells);
		
		if (userAnswer === correctAnswer) {
			msg("Right! The answer is " + correctAnswer + ".","green");
		} else {
			msg("Sorry. The correct answer is " + correctAnswer + ".","red");
		}
		removeOption();
		questionChanged();
	}
	
	function getAnswer() {
		var i = document.Quiz.question.selectedIndex;
		var answer = document.Quiz.question[i].value;
		return answer;
	}
	
	function getQuestion(){
		var i = document.Quiz.question.selectedIndex;
		var question = document.Quiz.question[i].text;
		return question;
	}
	
	function removeOption() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		question.remove(question.selectedIndex);
		if(question.options.length == 1) {
			category.remove(category.selectedIndex);
			if (category.options.length == 0) {
				endQuiz();
			} else {
				selChanged(category,categories,question);
			}
		}
		resetTimer(timePerQuestion);
	}
	
	function questionChanged() {
		document.Quiz.answer.value = "";
		var timerRow,answerRow;
		if (document.Quiz.question.selectedIndex === 0) {
			document.Quiz.btnCheck.disabled = true;
			document.Quiz.answer.disabled = true;
			resetTimer(timePerQuestion);
		} else {
			document.Quiz.btnCheck.disabled = false;
			document.Quiz.answer.disabled = false;
			timer = setInterval(decrementTimer,1000);
			timerRow = document.getElementById("timerRow");
			timerRow.style.display = "";
			answerRow = document.getElementById("answerRow");
			answerRow.style.display = "";
			document.Quiz.answer.focus();
		}
	}
	
	function endQuiz() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		resetTimer(timePerQuestion);
		alert("Thanks for playing! The quiz will now reload.");
		category.add(new Option("Addition","addition"));
		category.add(new Option("Subtraction","subtraction"));
		category.add(new Option("Multiplication","multiplication"));
		category.add(new Option("Division","division"));
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}
	
	function msg(text, color) {
		document.getElementById("msg").innerHTML = text;
		document.getElementById("msg").style.color = color;
	}
</script>
</head>
<body>
<form name="Quiz" onsubmit="return false;">
<table>
<tr>
	<td>Category:</td>
	<td>
		<select name="category" id="category">
			<option value="addition">Addition</option>
			<option value="subtraction">Subtraction</option>
			<option value="multiplication">Multiplication</option>
			<option value="division">Division</option>
		</select>
	</td>
</tr>
<tr>
	<td>Question:</td>
	<td>
		<select name="question" id="question">
			<option value="0">--Please Choose--</option>
		</select>
	</td>
</tr>
<tr id="answerRow">
	<td>Answer:</td>
	<td>
		<input type="text" name="answer" id="answer" 
			size="2" disabled="disabled">
		<input type="button" name="btnCheck" id="btnCheck" 
			value="Check Answer" disabled="disabled">
	</td>
</tr>
<tr id="timerRow">
	<td>Timer:</td>
	<td><input type="text" name="timeLeft" id="timeLeft" size="2"> seconds left</td>
</tr>
<tr>
	<td id="msg" colspan="2">Good luck!</td>
</tr>
</table>
</form>
<hr>
<table id="tblResults">
<tr>
	<th>Question</th>
	<th>Your answer</th>
	<th>Correct answer</th>
</tr>
</table>
</body>
</html>

Code Explanation

Challenge Solution:

CSSObjectModel/Solutions/MathQuizTable-challenge.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Math Quiz</title>
<link href="MathQuiz.css" rel="stylesheet">
<script src="questions.js"></script>
<script>	
	var timer, timePerQuestion = 10;
	window.onload = function() {
		init();
	}
	
	function init() {
		var category = document.getElementById("category");
		var question = document.getElementById("question");
		var btnCheck = document.getElementById("btnCheck");
		var timeLeft = document.getElementById("timeLeft");
		category.addEventListener('change', function() {
			selChanged(category,categories,question);
			resetTimer(timePerQuestion);
		});
		question.addEventListener('change', function() {
			questionChanged();
		});
		btnCheck.addEventListener('click', function() {
			checkAnswer();
		});
		timeLeft.addEventListener('focus', function() {
			timeLeft.blur();
		});
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}

	function selChanged(sel,data,dependentSel) {
		var selection = sel.options[sel.selectedIndex].value;
		var arrOptions = data[selection];
		var opt;
		dependentSel.options.length = 1;
		for (var i in arrOptions) {
			opt = new Option(arrOptions[i].q,arrOptions[i].a);
			dependentSel.add(opt);
		}
	}
	
	function resetTimer(seconds) {
		var timerRow = document.getElementById("timerRow");
		var answerRow = document.getElementById("answerRow");
		timerRow.style.display = "none";
		answerRow.style.display = "none";
		document.getElementById("timeLeft").value = seconds;
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var timeLeft = document.getElementById("timeLeft");
		timeLeft.value--;
		if (timeLeft.value < 0) {
			resetTimer(timePerQuestion);
			msg("Time's up!  The answer is " + getAnswer() + ".","red");
			removeOption();
		}
	}

	function addRow(tableId, cells) {
		var tableElem = document.getElementById(tableId);
		var newRow = tableElem.insertRow(tableElem.rows.length);
		var newCell;
		for (var i = 0; i < cells.length; i++) {
			newCell = newRow.insertCell(newRow.cells.length);
			newCell.innerHTML = cells[i];
		}
		return newRow;
	}

	function checkAnswer() {
		var userAnswer = document.getElementById("answer").value;
		var correctAnswer = getAnswer();
		var question = getQuestion();
		var arrCells = [question, userAnswer, correctAnswer];
		var row = addRow("tblResults", arrCells);
		
		if (userAnswer === correctAnswer) {
			msg("Right! The answer is " + correctAnswer + ".","green");
			row.style.backgroundColor="#00ff00";
		} else {
			msg("Sorry. The correct answer is " + correctAnswer + ".","red");
			row.style.backgroundColor="#ff9999";
		}
		removeOption();
		questionChanged();
	}
	
	function getAnswer() {
		var i = document.Quiz.question.selectedIndex;
		var answer = document.Quiz.question[i].value;
		return answer;
	}
	
	function getQuestion(){
		var i = document.Quiz.question.selectedIndex;
		var question = document.Quiz.question[i].text;
		return question;
	}
	
	function removeOption() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		question.remove(question.selectedIndex);
		if(question.options.length == 1) {
			category.remove(category.selectedIndex);
			if (category.options.length == 0) {
				endQuiz();
			} else {
				selChanged(category,categories,question);
			}
		}
		resetTimer(timePerQuestion);
	}
	
	function questionChanged() {
		document.Quiz.answer.value = "";
		var timerRow,answerRow;
		if (document.Quiz.question.selectedIndex === 0) {
			document.Quiz.btnCheck.disabled = true;
			document.Quiz.answer.disabled = true;
			resetTimer(timePerQuestion);
		} else {
			document.Quiz.btnCheck.disabled = false;
			document.Quiz.answer.disabled = false;
			timer = setInterval(decrementTimer,1000);
			timerRow = document.getElementById("timerRow");
			timerRow.style.display = "";
			answerRow = document.getElementById("answerRow");
			answerRow.style.display = "";
			document.Quiz.answer.focus();
		}
	}
	
	function endQuiz() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		resetTimer(timePerQuestion);
		alert("Thanks for playing! The quiz will now reload.");
		category.add(new Option("Addition","addition"));
		category.add(new Option("Subtraction","subtraction"));
		category.add(new Option("Multiplication","multiplication"));
		category.add(new Option("Division","division"));
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}
	
	function msg(text, color) {
		document.getElementById("msg").innerHTML = text;
		document.getElementById("msg").style.color = color;
	}
</script>
</head>
<body>
<form name="Quiz" onsubmit="return false;">
<table>
<tr>
	<td>Category:</td>
	<td>
		<select name="category" id="category">
			<option value="addition">Addition</option>
			<option value="subtraction">Subtraction</option>
			<option value="multiplication">Multiplication</option>
			<option value="division">Division</option>
		</select>
	</td>
</tr>
<tr>
	<td>Question:</td>
	<td>
		<select name="question" id="question">
			<option value="0">--Please Choose--</option>
		</select>
	</td>
</tr>
<tr id="answerRow">
	<td>Answer:</td>
	<td>
		<input type="text" name="answer" id="answer" 
			size="2" disabled="disabled">
		<input type="button" name="btnCheck" id="btnCheck" 
			value="Check Answer" disabled="disabled">
	</td>
</tr>
<tr id="timerRow">
	<td>Timer:</td>
	<td><input type="text" name="timeLeft" id="timeLeft" size="2"> seconds left</td>
</tr>
<tr>
	<td id="msg" colspan="2">Good luck!</td></tr>
</tr>
</table>
</form>
<hr>
<table id="tblResults">
<tr>
	<th>Question</th>
	<th>Your answer</th>
	<th>Correct answer</th>
</tr>
</table>
</body>
</html>

Dynamically Changing Dimensions

The dimensions of an object can be changed by modifying the width and height properties of the element's style property. The following example demonstrates this:

Code Sample:

CSSObjectModel/Demos/Dimensions.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Dimensions</title>
<link href="dimensions.css" rel="stylesheet">
<script>
	function grow(elem){
		var curWidth = parseInt(getComputedStyle(elem).getPropertyValue("width"));
		var curHeight = parseInt(getComputedStyle(elem).getPropertyValue("height"));
		elem.style.width = (curWidth * 1.5) + 'px';
		elem.style.height = (curHeight * 1.5) + 'px';
		showDimensions(elem);
	}
	
	function shrink(elem){
		var curWidth = parseInt(getComputedStyle(elem).getPropertyValue("width"));
		var curHeight = parseInt(getComputedStyle(elem).getPropertyValue("height"));
		elem.style.width = (curWidth / 1.5) + 'px';
		elem.style.height = (curHeight / 1.5) + 'px';
		showDimensions(elem);
	}
	
	function showDimensions(elem) {
		elem.innerHTML = "w: " + getComputedStyle(elem).getPropertyValue("width") + "<br>h: " + getComputedStyle(elem).getPropertyValue("height");
	}

	window.onload = function() {
		var block = document.getElementById("divBlock");
		block.addEventListener('mouseover', function() {
			grow(block);
		})
		block.addEventListener('mouseout', function() {
			shrink(block);
		})
		showDimensions(block);
	};
</script>
</head>
<body>
<div id="divBlock"></div>
</body>
</html>

Code Explanation

When the page loads, we begin observing mouseover and mouseout events on the block div. We call grow() on mouse overs and shrink() on mouse outs.

The grow() function uses parseInt() to cut off the units (e.g., px) from the value of the width and height of the div and assigns the resulting integers to variables: curWidth and curHeight. It then modifies the width and height properties of the element by multiplying the current values by 1.5.

The shrink() function does the same thing, but it divides by 1.5 instead of multiplying.

Creating a Timed Slider

The example below shows how a timed slider can be created by dynamically changing an element's dimensions:

Code Sample:

CSSObjectModel/Demos/Slider.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Slider</title>
<link href="slider.css" rel="stylesheet">
<script>
	var timer, timesUp;
	function resetTimer() {
		var slider = document.getElementById("divSlider");
		timesUp = true;
		slider.style.width = "0px";
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var slider = document.getElementById("divSlider");
		var curWidth = parseInt(getComputedStyle(slider).getPropertyValue("width"));
		timesUp = false;
		if (curWidth < 200) {
			slider.style.width = curWidth + 2 + "px";
		} else {
			alert("Time's up!");
			resetTimer();
		}
	}

	window.onload = function() {
		var btnStart = document.getElementById("btnStart");
		resetTimer();
		btnStart.addEventListener('click', function() {
			timer = setInterval(decrementTimer, 100);
		});
	};
</script>
</head>
<body>
<div id="divSliderBG">
	<div id="divSlider"></div>
</div>
<button id="btnStart">Start Timer</button>
</body>
</html>

Positioning Elements Dynamically

The position of an object can be changed by modifying the left and top properties of the element's style property. The following example demonstrates this:

Code Sample:

CSSObjectModel/Demos/Position.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Position</title>
<link href="position.css" rel="stylesheet">
<script>	
	function moveH(elem, distance){
		var curLeft = parseInt(getComputedStyle(elem).getPropertyValue("left"));
		elem.style.left = (curLeft + distance) + "px";
	}
	
	function moveV(elem, distance){
		var curTop = parseInt(getComputedStyle(elem).getPropertyValue("top"));
		elem.style.top = (curTop + distance) + "px";
	}

	window.onload = function() {
		var btnLeft = document.getElementById("btnLeft");
		var btnRight = document.getElementById("btnRight");
		var btnUp = document.getElementById("btnUp");
		var btnDown = document.getElementById("btnDown");
		var block = document.getElementById("divBlock");
		
		btnLeft.addEventListener('click', function() {
			moveH(block,-10);
		});
		btnRight.addEventListener('click', function() {
			moveH(block,10);
		});
		btnUp.addEventListener('click', function() {
			moveV(block,-10);
		});
		btnDown.addEventListener('click', function() {
			moveV(block,10);
		});
	};
</script>
</head>
<body>
<div id="field">
	<button id="btnLeft">Left</button>
	<button id="btnRight">Right</button>
	<button id="btnUp">Up</button>
	<button id="btnDown">Down</button>
	<div id="divBlock"></div>
</div>
</body>
</html>

Code Explanation

When the page loads, we begin observing click events on the buttons to call moveH() and moveV().

The moveH() function uses parseInt() to cut off the units (e.g., px) from the value of the left property and assigns the resulting integers to the curLeft variable. It then adds the passed-in distance to the left property.

The moveV() function does the same thing, but it modifies the top property rather than the left property.

Creating a Different Timed Slider

The example below shows how a different type of timed slider can be created by dynamically changing an element's position:

Code Sample:

CSSObjectModel/Demos/Slider2.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Slider</title>
<link href="slider2.css" rel="stylesheet">
<script>
	var timer, timesUp;
	function resetTimer(){
		var slider = document.getElementById("divSlider");
		timesUp = true;
		slider.style.left = "1px";
		clearInterval(timer);
	}
	
	function decrementTimer(){
		var slider = document.getElementById("divSlider");
		var curLeft = parseInt(getComputedStyle(slider).getPropertyValue("left"));
		var curWidth = parseInt(getComputedStyle(slider).getPropertyValue("width"));
		timesUp = false;
		if (curLeft < 198 - curWidth) {
			slider.style.left = curLeft + 2 + "px";
		} else {
			alert("Time's up!");
			resetTimer();
		}
	}

	window.onload = function() {
		var btnStart = document.getElementById("btnStart");
		resetTimer();
		btnStart.addEventListener('click', function() {
			timer = setInterval(decrementTimer, 100);
		});
	};
</script>
</head>
<body>
<div id="divSliderBG">
	<div id="divSlider"></div>
</div>
<button id="btnStart">Start Timer</button>
</body>
</html>

Changing the Math Quiz Timer to a Slider

Duration: 15 to 25 minutes.

In this exercise, you will modify the Math Quiz so that the timer is a slider rather than a count down. The result will look like this: Math Quiz Slider

  1. Open CSSObjectModel/Exercises/MathQuizSlider.html for editing.
  2. Notice that the timer on the page has been changed from an input element to two divs.
    <tr id="timerRow">
    		<td>Timer:</td>
    		<td>
    		<div id="divSliderBG">
    		<div id="divSlider"></div>
    		</div>
    		</td>
    		</tr>
  3. Also, all references to the timeLeft input have been removed and the code within the decrementTimer() function has been commented out.
  4. Rewrite the decrementTimer() function to use a slider like the one shown in the screenshot above.
  5. Test your solution in a browser.

Solution:

CSSObjectModel/Solutions/MathQuizSlider.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Math Quiz</title>
<link href="MathQuiz.css" rel="stylesheet">
<script src="questions.js"></script>
<script>	
	var timer, timePerQuestion = 10;
	window.onload = function() {
		init();
	}
	
	function init() {
		var category = document.getElementById("category");
		var question = document.getElementById("question");
		var btnCheck = document.getElementById("btnCheck");
		category.addEventListener('change', function() {
			selChanged(category,categories,question);
			resetTimer(timePerQuestion);
		});
		question.addEventListener('change', function() {
			questionChanged();
		});
		btnCheck.addEventListener('click', function() {
			checkAnswer();
		});
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}

	function selChanged(sel,data,dependentSel) {
		var selection = sel.options[sel.selectedIndex].value;
		var arrOptions = data[selection];
		var opt;
		dependentSel.options.length = 1;
		for (var i in arrOptions) {
			opt = new Option(arrOptions[i].q,arrOptions[i].a);
			dependentSel.add(opt);
		}
	}
	
	function resetTimer(seconds) {
		var slider = document.getElementById("divSlider");
		var timerRow = document.getElementById("timerRow");
		var answerRow = document.getElementById("answerRow");
		slider.style.left = "1px";
		timerRow.style.display = "none";
		answerRow.style.display = "none";
		clearInterval(timer);
	}
	
	function decrementTimer() {
		var slider = document.getElementById("divSlider");
		var curLeft = parseInt(getComputedStyle(slider).getPropertyValue("left"));
		var curWidth = parseInt(getComputedStyle(slider).getPropertyValue("width"));
		if (curLeft < 198 - curWidth) {
			slider.style.left = curLeft + 2 + "px";
		} else {
			resetTimer(timePerQuestion);
			msg("Time's up!  The answer is " + getAnswer() + ".","red");
			removeOption();
		}
	}

	function addRow(tableId, cells){
		var tableElem = document.getElementById(tableId);
		var newRow = tableElem.insertRow(tableElem.rows.length);
		var newCell;
		for (var i = 0; i < cells.length; i++) {
			newCell = newRow.insertCell(newRow.cells.length);
			newCell.innerHTML = cells[i];
		}
		return newRow;
	}

	function checkAnswer() {
		var userAnswer = document.getElementById("answer").value;
		var correctAnswer = getAnswer();
		var question = getQuestion();
		var arrCells = [question, userAnswer, correctAnswer];
		var row = addRow("tblResults", arrCells);
		
		if (userAnswer === correctAnswer) {
			msg("Right! The answer is " + correctAnswer + ".","green");
			row.style.backgroundColor="#00ff00";
		} else {
			msg("Sorry. The correct answer is " + correctAnswer + ".","red");
			row.style.backgroundColor="#ff9999";
		}
		removeOption();
		questionChanged();
	}
	
	function getAnswer() {
		var i = document.Quiz.question.selectedIndex;
		var answer = document.Quiz.question[i].value;
		return answer;
	}
	
	function getQuestion(){
		var i = document.Quiz.question.selectedIndex;
		var question = document.Quiz.question[i].text;
		return question;
	}
	
	function removeOption() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		question.remove(question.selectedIndex);
		if(question.options.length == 1) {
			category.remove(category.selectedIndex);
			if (category.options.length == 0) {
				endQuiz();
			} else {
				selChanged(category,categories,question);
			}
		}
		resetTimer(timePerQuestion);
	}
	
	function questionChanged() {
		document.Quiz.answer.value = "";
		var timerRow,answerRow;
		if (document.Quiz.question.selectedIndex === 0) {
			document.Quiz.btnCheck.disabled = true;
			document.Quiz.answer.disabled = true;
			resetTimer(timePerQuestion);
		} else {
			document.Quiz.btnCheck.disabled = false;
			document.Quiz.answer.disabled = false;
			timer = setInterval(decrementTimer,100);
			timerRow = document.getElementById("timerRow");
			timerRow.style.display = "";
			answerRow = document.getElementById("answerRow");
			answerRow.style.display = "";
			document.Quiz.answer.focus();
		}
	}
	
	function endQuiz() {
		var category = document.Quiz.category;
		var question = document.Quiz.question;
		resetTimer(timePerQuestion);
		alert("Thanks for playing! The quiz will now reload.");
		category.add(new Option("Addition","addition"));
		category.add(new Option("Subtraction","subtraction"));
		category.add(new Option("Multiplication","multiplication"));
		category.add(new Option("Division","division"));
		selChanged(category,categories,question);
		resetTimer(timePerQuestion);
	}
	
	function msg(text, color) {
		document.getElementById("msg").innerHTML = text;
		document.getElementById("msg").style.color = color;
	}
</script>
</head>
<body>
<form name="Quiz" onsubmit="return false;">
<table>
<tr>
	<td>Category:</td>
	<td>
		<select name="category" id="category">
			<option value="addition">Addition</option>
			<option value="subtraction">Subtraction</option>
			<option value="multiplication">Multiplication</option>
			<option value="division">Division</option>
		</select>
	</td>
</tr>
<tr>
	<td>Question:</td>
	<td>
		<select name="question" id="question">
			<option value="0">--Please Choose--</option>
		</select>
	</td>
</tr>
<tr id="answerRow">
	<td>Answer:</td>
	<td>
		<input type="text" name="answer" id="answer" 
			size="2" disabled="disabled">
		<input type="button" name="btnCheck" id="btnCheck" 
			value="Check Answer" disabled="disabled">
	</td>
</tr>
<tr id="timerRow">
	<td>Timer:</td>
	<td>
		<div id="divSliderBG">
			<div id="divSlider"></div>
		</div>
	</td>
</tr>
<tr>
	<td id="msg" colspan="2">Good luck!</td>
</tr>
</table>
</form>
<hr>
<table id="tblResults">
<tr>
	<th>Question</th>
	<th>Your answer</th>
	<th>Correct answer</th>
</tr>
</table>
</body>
</html>

Changing the Z-Index

The z-index value of an element indicates its relative position in the "stack" of elements on the page. Elements with higher z-index values sit on top of elements with lower values. You can think of a stack of papers thrown on a table. The ones at the top have a higher z-index than the ones on the bottom. In the screenshot below, the blue box has a higher z-index than the red box: Z Index

The z-index of an object can be changed by modifying the zIndex property of the element's style property. The following example demonstrates this:

Code Sample:

CSSObjectModel/Demos/Zindex.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>zIndex</title>
<link href="zindex.css" rel="stylesheet">
<script>
	var z = 0;
	function changeZ(elem){
		z += 10;
		elem.style.zIndex = z;
		elem.innerHTML = "z: " + z;
	}

	window.onload = function() {
		var divRed = document.getElementById("divRed");
		var divBlue = document.getElementById("divBlue");
		divRed.addEventListener('click', function() {
			changeZ(divRed);
		});
		divBlue.addEventListener('click', function() {
			changeZ(divBlue);
		});
	};
</script>
</head>
<body>
	<div id="divRed"></div>
	<div id="divBlue"></div>
</body>
</html>

Code Explanation

The variable z always holds the highest z-index. The function changeZ() simply adds 10 to z and assigns the resulting value to the zIndex property of the passed in object.

function changeZ(elem){
z += 10;
elem.style.zIndex = z;
elem.innerHTML = "z: " + z;
}

The CSS Object Model

Behind all of our work manipulating and reading CSS styles with JavaScript lies the CSS Object Model API, a complete (and ever changing) specification from the W3C that defines how to control CSS with JavaScript. The complete specification is at https://www.w3.org/TR/cssom/; you may also find useful the Mozilla Developer Networ's CSS Object Model documentation.

While delving too deeply into the API itself is beyond the scope of this course, we will take a quick look at a way in which we can check to see if a user's browser offers support for a given CSS property and value. Consider the following example:

Code Sample:

CSSObjectModel/Demos/supports.html
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8">
<title>Supports</title>
<script>
	window.onload = function() {
		document.getElementById("checkit").addEventListener("click", function() {
			var p = document.getElementById("p").value;
			var v = document.getElementById("v").value;
			if (CSS.supports(p, v)) {
				alert('Your browser DOES support property ' + p + ' with value ' + v);
			} else {
				alert('Your browser does NOT support property ' + p + ' with value ' + v);
			}
			return false;
		});
	}
</script>
</head>
<body>
<h1>Supports</h1>
<form action="#">
	<input type="text" id="p" placeholder="Property"> <input type="text" id="v" placeholder="Value">
	<button id="checkit">Check</button>
</form>
</body>
</html>

Code Explanation

The page presents two textfields in which the user can enter a CSS property and a value for that property. Clicking the "Check" button fires an event handler which includes the code CSS.supports(p, v); an alert indicates whether the current browser does or does not support the given property/value.

As of this writing, you can try property hyphens and value manual: the current version of Chrome does not support that property but the current version of Firefox does support it.