Hi,
I have been using the JS handler onchange to populate an options menu based on a selection from another select menu. But is it possible to call a JS function to populate such a menu before any menu is selected? I was looking at handlers on MDN. Would onshow be suitable or is there anything better?
Maybe I should think about using JS to create the entire element when the page loads.
Thanks,
Shane
It depends what you are trying to do.
It could well be enough for you to simulate a change event on the first menu once it is loaded. This will then populate the second one without any user interaction:
$("#myMenu").trigger("change");
Hi Pullo, thanks fro your reply.
I have a menu of units which are just numbers from 1-8. Each unit contains specific topics. So when a unit is selected only certain topics can be selected from a second menu, the topics menu.
Then this repeats on the next two menues, topics and subtopics. So that if a certain topic is selected only specific subtopics can be selected from a third select menu.
This does work, but there is an issue. When the unit is selected, the second menu correctly allows only certain topics. But at this stage the third menu is showing subtopics not associated with the current topics. This is because, although the second menu has changed it does not trigger the âonchangeâ in it to set the third menu.
Here is the code,
<?php include_once $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/helpers.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildtopics.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildsubtopics.inc.php'; ?>
<!DOCTYPE html>
<html lang="en">
<head>
<?php
$units = range(0, 8);
?>
<script>
var newUnits = <?php echo json_encode($units, JSON_PRETTY_PRINT) ?>;
var newTopics = <?php echo json_encode($topics, JSON_PRETTY_PRINT) ?>;//converts php arrays to JS object
var newSubTopics = <?php echo json_encode($subtopics, JSON_PRETTY_PRINT) ?>;
function obj2Array(obj) {
return Object.keys(obj).map(function (key) {return obj[key]});
}
newUnits = obj2Array(newUnits);
function populateOne(s1, s2){
console.log(newUnits.length);
var s1 = document.getElementById(s1);
var s2 = document.getElementById(s2);
var optionArray = [];
s2.innerHTML = "";
for (i = 0; i < newTopics.length; i++) {
if(s1.value == newTopics[i].unit){
optionArray.push(newTopics[i].topic);
}
}
for(var option in optionArray){
var newOption = document.createElement("option");
newOption.value = optionArray[option];
newOption.innerHTML = optionArray[option];
s2.options.add(newOption);
}
function populate(s3, s4){
var s3 = document.getElementById(s3);
var s4 = document.getElementById(s4);
var optionArray = [];
s4.innerHTML = "";
for (i = 0; i < newTopics.length; i++) {
for (j = 0; j < newSubTopics.length; j++) {
if(newTopics[i].id == newSubTopics[j].topicid){
if(s3.value == newTopics[i].topic){
optionArray.push(newSubTopics[j].subtopic);
}
}
}
}
for(var option in optionArray){
var newOption = document.createElement("option");
newOption.value = optionArray[option];
newOption.innerHTML = optionArray[option];
s4.options.add(newOption);
}
}
</script>
<link type="text/css" rel="stylesheet" href="/artgibney/css/stylesheet.css"/>
<meta charset="utf-8">
<title><?php htmlout($pageTitle); ?></title>
</head>
<body>
<link rel="icon" type="image/x-icon" href="http://www.artgibney.com/artgibney/images/favicon.ico"/>
<div class="formpts">
<div>
<p><a href="..">physCMS home</a> ⇒ <a href="/artgibney/admin/teachingpoints/">Teaching Points</a></p>
<h1><?php htmlout($pageTitle); ?></h1>
</div>
<form action="?<?php htmlout($action); ?>" method="post">
<div>
<table>
<tr>
<th>Unit</th>
</tr>
<tr>
<td>
<select name="unit" id="unit" style="background-color:transparent" onchange="populateOne('unit', 'topic','subtopic')">
<?php foreach($units as $key=>$option):
$selected = ($unit == $key) ? 'selected' : '';
echo "<option value='$key' $selected>$option</option>";
endforeach; ?>
</select>
</td>
</tr>
</table>
</div>
<br>
<div>
<table>
<tr>
<th>Topic</th>
<th>Sub-topic</th>
</tr>
<tr>
<td>
<select name="topic" id="topic" style="background-color:transparent" onchange="populate('topic', 'subtopic')">
<option value=""></option>
<?php foreach($topics as $topical):unit as the unit above.
$value = $topical['topic'];
$selected = ($topic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
endforeach; ?>
</select>
</td>
<td>
<select name="subtopic" id="subtopic" style="background-color:transparent">
<option value=""></option>
<?php foreach($subtopics as $subtopical):
$value = $subtopical['subtopic'];
$selected = ($subtopic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
endforeach; ?>
</select>
</td>
</tr>
</table>
<br>
<input type="hidden" name="id" value="<?php htmlout($id); ?>">
<input type="submit" value="<?php htmlout($button); ?>">
</div>
</form>
</div>
</body>
</html>
I know the JS should be in a separate file, but it is just hander for now this way.
Thanks,
Shane
After a lot of cleaning up we have a working example. Iâve left the includes in there which will provide warnings for others, but for the OP who already has the files things should keep on working fine.
Many issues were had with the code, but after some troubleshooting the menus are initially populated as desired, and the subtopics are changing when others above them are updated too.
The main idea here is that when one of them are changed and triggers a handler, such as unitHandler, it updates the topic and then triggers the topic change event. This allows the topic handler to carry on and update the subtopic one.
function unitHandler() {
var reference = this,
parent = reference.value;
updateSelect('topic', newTopics, parent);
document.getElementById('topic').onchange();
};
In order to get things working Iâve been forced to add in some supporting PHP code. The comments surrounding that section are easy to find, so that you can remove them to get things working with your pre-existing setup.
Hereâs the full test code:
<?php include_once $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/helpers.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildtopics.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildsubtopics.inc.php'; ?>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
$units = range(0, 8);
// ===============================
// Start of added PHP support code
// Remove this section
// ===============================
function htmlout($content) {
echo htmlentities($content);
}
$topics = [
["parent" => "1", "name" => "topic 1.1", "sub" => "topic11"],
["parent" => "1", "name" => "topic 1.2", "sub" => "topic12"],
["parent" => "1", "name" => "topic 1.3", "sub" => "topic13"],
["parent" => "2", "name" => "topic 2.1", "sub" => "topic21"],
["parent" => "2", "name" => "topic 2.2", "sub" => "topic22"],
["parent" => "2", "name" => "topic 2.3", "sub" => "topic23"],
["parent" => "3", "name" => "topic 3.1", "sub" => "topic31"],
["parent" => "3", "name" => "topic 3.2", "sub" => "topic32"],
["parent" => "3", "name" => "topic 3.3", "sub" => "topic33"]
];
$subtopics = [
["parent" => "topic11", "name" => "subtopic 1.1.1"],
["parent" => "topic11", "name" => "subtopic 1.1.2"],
["parent" => "topic11", "name" => "subtopic 1.1.3"],
["parent" => "topic12", "name" => "subtopic 1.2.1"],
["parent" => "topic12", "name" => "subtopic 1.2.2"],
["parent" => "topic12", "name" => "subtopic 1.2.3"],
["parent" => "topic13", "name" => "subtopic 1.3.1"],
["parent" => "topic13", "name" => "subtopic 1.3.2"],
["parent" => "topic13", "name" => "subtopic 1.3.3"],
["parent" => "topic21", "name" => "subtopic 2.1.1"],
["parent" => "topic21", "name" => "subtopic 2.1.2"],
["parent" => "topic21", "name" => "subtopic 2.1.3"],
["parent" => "topic22", "name" => "subtopic 2.2.1"],
["parent" => "topic22", "name" => "subtopic 2.2.2"],
["parent" => "topic22", "name" => "subtopic 2.2.3"],
["parent" => "topic23", "name" => "subtopic 2.3.1"],
["parent" => "topic23", "name" => "subtopic 2.3.2"],
["parent" => "topic23", "name" => "subtopic 2.3.3"],
["parent" => "topic31", "name" => "subtopic 3.1.1"],
["parent" => "topic31", "name" => "subtopic 3.1.2"],
["parent" => "topic31", "name" => "subtopic 3.1.3"],
["parent" => "topic32", "name" => "subtopic 3.2.1"],
["parent" => "topic32", "name" => "subtopic 3.2.2"],
["parent" => "topic32", "name" => "subtopic 3.2.3"],
["parent" => "topic33", "name" => "subtopic 3.3.1"],
["parent" => "topic33", "name" => "subtopic 3.3.2"],
["parent" => "topic33", "name" => "subtopic 3.3.3"],
];
$pageTitle = "Test page";
$action = "index.php";
$unit = "3";
$topic = "topic 3.1";
$subtopic = "subtopic 3.2.3";
$id = "hidden id";
$button = 'Submit';
// =============================
// End of added PHP support code
// =============================
?>
<link type="text/css" rel="stylesheet" href="/artgibney/css/stylesheet.css" />
<meta charset="utf-8" />
<title><?php htmlout($pageTitle); ?></title>
<link rel="icon" type="image/x-icon" href="http://www.artgibney.com/artgibney/images/favicon.ico" />
</head>
<body>
<div class="formpts">
<div>
<p><a href="..">physCMS home</a> ⇒ <a href="/artgibney/admin/teachingpoints/">Teaching Points</a></p>
<h1><?php htmlout($pageTitle); ?></h1>
</div>
<form action="<?php htmlout($action);?>" method="post">
<div>
<table>
<tr>
<th>Unit</th>
</tr>
<tr>
<td><select name="unit" id="unit" style="background-color:transparent">
<?php for ($i = 1; $i < count($units); $i += 1) {
$selected = ($unit == $i) ? 'selected' : '';
echo "<option value='$i' $selected>{$units[$i]}</option>";
} ?>
</select></td>
</tr>
</table>
</div><br />
<div>
<table>
<tr>
<th>Topic</th>
<th>Sub-topic</th>
</tr>
<tr>
<td><select name="topic" id="topic" style="background-color:transparent">
<?php
$validSubtopics = [];
foreach($topics as $topical): // unit as the unit above.
if ($topical['parent'] === $unit) {
$value = $topical['name'];
$selected = ($topic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
array_push($validSubtopics, $topical['sub']);
}
endforeach; ?>
</select></td>
<td><select name="subtopic" id="subtopic" style="background-color:transparent">
<?php foreach($subtopics as $subtopical):
if (in_array($subtopical['parent'], $validSubtopics)) {
$value = $subtopical['name'];
$selected = ($subtopic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
}
endforeach; ?>
</select></td>
</tr>
</table><br />
<input type="hidden" name="id" value="<?php htmlout($id); ?>" /> <input type="submit" value="<?php htmlout($button); ?>" />
</div>
</form>
</div>
<script>
var newUnits = <?php echo json_encode($units, JSON_PRETTY_PRINT) ?>;
var newTopics = <?php echo json_encode($topics, JSON_PRETTY_PRINT) ?>;
var newSubTopics = <?php echo json_encode($subtopics, JSON_PRETTY_PRINT) ?>;
function getSubId(source, name) {
return source.reduce(function (options, option) {
if (option.name === name) {
options = option.sub;
}
return options;
}, []);
}
function getOptionsByParent(source, referenceId) {
return source.reduce(function (options, option) {
if (option.parent === referenceId) {
options.push(option.name);
}
return options;
}, []);
}
function updateOptions(target, options) {
target.innerHTML = "";
options.forEach(function (option) {
var newOption = document.createElement("option");
newOption.value = option;
newOption.innerHTML = option;
target.options.add(newOption);
});
}
function updateSelect(target, source, parent) {
var target = document.getElementById(target),
optionArray = getOptionsByParent(source, parent);
updateOptions(target, optionArray);
}
// event handlers
function topicHandler(evt) {
var reference = this,
parent;
parent = getSubId(newTopics, reference.value);
updateSelect('subtopic', newSubTopics, parent);
};
function unitHandler() {
var reference = this,
parent = reference.value;
updateSelect('topic', newTopics, parent);
document.getElementById('topic').onchange();
};
document.getElementById('topic').onchange = topicHandler;
document.getElementById('unit').onchange = unitHandler;
</script>
</body>
</html>
Well this is fantastic and thnak you very much for writing this code.
Unfortunately, I canât get it to work. When I comment out the extra php I just get a blank page. The $topics and $subtopics arrays should be built by the include files.
So I tried commenting out those two include file and leaving the extra php uncomented. But still a blank page.
Then I uncommented everything except
/*function htmlout($content) {
echo htmlentities($content);
}*/
It works with the new arrays,
I know the above table looks really odd, having way too much room for the units menu. But actually there are many more menus. I just took out the others as they are irrelevant here.
looking at the code, I can basically see what is going on, but the first thing I donât get is the âthisâ and what does this bit do in the unitHandler function?
var reference = this,
parent = reference.value;
or where could I read about this? I imagine it is value of the current topic or something like that.
I wonder why this doesnât work when I comment out the php arrays in your code and allow the include files? Actually of course it is because my arrays donât look like this.
So i will need to understand the code better to get it to work with my arrays.
My arrays look like this,
Array
(
[0] => Array
(
[id] => 60
[topic] => Waves
[unit] => 2
)
[1] => Array
(
[id] => 57
[topic] => Mechanics
[unit] => 1
)
[2] => Array
(
[id] => 62
[topic] => Further mechanics
[unit] => 3
)
[3] => Array
(
[id] => 61
[topic] => DC Electricity
[unit] => 2
)
[4] => Array
(
[id] => 59
[topic] => Materials
[unit] => 1
)
[5] => Array
(
[id] => 63
[topic] => Electric fields
[unit] => 3
)
[6] => Array
(
[id] => 64
[topic] => Magnetic fields
[unit] => 3
)
)
Array
(
[0] => Array
(
[id] => 80
[subtopic] => Equations of motion
[topicid] => 57
[currno] => 1
)
[1] => Array
(
[id] => 81
[subtopic] => Vectors
[topicid] => 57
[currno] => 2
)
[2] => Array
(
[id] => 75
[subtopic] => Roughness
[topicid] => 53
[currno] => 9
)
[3] => Array
(
[id] => 76
[subtopic] => Smoothness
[topicid] => 53
[currno] => 7
)
[4] => Array
(
[id] => 82
[subtopic] => Newton's second law
[topicid] => 57
[currno] => 3
)
[5] => Array
(
[id] => 83
[subtopic] => Wave equation
[topicid] => 60
[currno] => 4
)
[6] => Array
(
[id] => 84
[subtopic] => Stoke's law
[topicid] => 59
[currno] => 7
)
[7] => Array
(
[id] => 85
[subtopic] => Terminal velocity
[topicid] => 59
[currno] => 5
)
[8] => Array
(
[id] => 86
[subtopic] => Young Modulus
[topicid] => 59
[currno] => 6
)
[9] => Array
(
[id] => 87
[subtopic] => Standing waves
[topicid] => 60
[currno] => 8
)
[10] => Array
(
[id] => 88
[subtopic] => Refractive index
[topicid] => 60
[currno] => 9
)
[11] => Array
(
[id] => 89
[subtopic] => Current
[topicid] => 61
[currno] => 10
)
[12] => Array
(
[id] => 90
[subtopic] => Voltage
[topicid] => 61
[currno] => 11
)
[13] => Array
(
[id] => 91
[subtopic] => Resistance
[topicid] => 61
[currno] => 12
)
[14] => Array
(
[id] => 92
[subtopic] => Momentum
[topicid] => 62
[currno] => 13
)
[15] => Array
(
[id] => 93
[subtopic] => Kinetic energy
[topicid] => 62
[currno] => 14
)
[16] => Array
(
[id] => 94
[subtopic] => Potential energy
[topicid] => 62
[currno] => 15
)
[17] => Array
(
[id] => 95
[subtopic] => Electric force
[topicid] => 63
[currno] => 16
)
[18] => Array
(
[id] => 96
[subtopic] => Electric field lines
[topicid] => 63
[currno] => 17
)
[19] => Array
(
[id] => 97
[subtopic] => Cappacity
[topicid] => 63
[currno] => 18
)
[20] => Array
(
[id] => 98
[subtopic] => Magnetic field lines
[topicid] => 64
[currno] => 19
)
[21] => Array
(
[id] => 99
[subtopic] => Fleming's left hand rule
[topicid] => 64
[currno] => 20
)
[22] => Array
(
[id] => 100
[subtopic] => Electromagnetic induction
[topicid] => 64
[currno] => 21
)
)
I think it will take me quite a while to understand this, so please donât think I am not bothering.
I really appreciate the code you sent.
Thanks,
Shane
Right I see now that the keys for the first array $topics are âparentâ, ânameâ and âsubâ
and for $subtopics they are âparentâ and ânameâ.
In my arrays the keys for $topic are âidâ, âtopicâ and âunitâ
and $subtopic they are âidâ, âsubtopicâ, âtopicidâ and âcurrnoâ.
I think I need to try to get the key âsubtopicâ into the array $topics.
Thanks,
Shane
Oops!
My topics table looks like this,
and the subtopics table looks like this,
Well I certainly have plenty to think about here.
Thanks,
Shane
What youâre building is typically called a linked list, chained select, or dependent select. There are jQuery plugins available that will do this for you. It can be a pita to build these correctly especially when the depth is infinite. Therefore, I would recommend using a jQuery plugin.
Ex.
Yes, I was working on making things more generic so that less duplicate code would be needed.
Now that the code is somewhat reasonable, Iâll use your existing database structure and modify things from there.
When an event occurs, it links to the element attached to that event via the this keyword.
The this keyword is also used in a wide variety of other situations too, which is why I like to use a named variable to store the reference, to help prevent large numbers of confusing this
references occuring throughout the code.
A good place to learn about this is from http://www.quirksmode.org/js/this.html
With a better representation of your database values, Iâve updated the code.
The inconsistent naming across fields has resulted in a little bit of extra duplication, such as:
function updateTopic(topic, topicDB, unitId) {
var optionArray = getTopicOptions(topicDB, unitId);
updateOptions(topic, optionArray);
}
function updateSubtopic(subtopic, subtopicDB, topicId) {
var optionArray = getSubtopicOptions(subtopicDB, topicId);
updateOptions(subtopic, optionArray);
}
but despite that, things are working much better now with your database schema.
Iâm quite pleased about how a 2-pass process using filter and map is replaced with just a reduce method instead. Previously it would be something like this:
function getTopicOptions(topicDB, unitId) {
var topics = topicDB.filter(function (option) {
return option.unit === unitId;
});
return topics.map(function (option) {
return option.topic;
});
}
But now, we can start with an empty array and use reduce to put the topic names in there.
function getTopicOptions(topicDB, unitId) {
return topicDB.reduce(function (options, option) {
if (option.unit === unitId) {
options.push(option.topic);
}
return options;
}, []);
}
Anyhow, hereâs the code as it stands now.
<?php /*include_once $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/helpers.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildtopics.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildsubtopics.inc.php';*/ ?>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
$units = range(0, 8);
// ===============================
// Start of added PHP support code
// Remove this section
// ===============================
function htmlout($content) {
echo htmlentities($content);
}
$topics = [
["id" => 60, "topic" => "Waves", "unit" => "2"],
["id" => 57, "topic" => "Mechanics", "unit" => "1"],
["id" => 62, "topic" => "Further mechanics", "unit" => "3"],
["id" => 61, "topic" => "DC Electricity", "unit" => "2"],
["id" => 59, "topic" => "Materials", "unit" => "1"],
["id" => 63, "topic" => "Electric fields", "unit" => "3"],
["id" => 64, "topic" => "Magnetic fields", "unit" => "3"]
];
$subtopics = [
["topicid" => 57, "subtopic" => "Equations of motion", "parent" => "mechanics", "name" => "subtopic 1.1.1"],
["topicid" => 57, "subtopic" => "Vectors", "parent" => "mechanics", "name" => "subtopic 1.1.2"],
["topicid" => 57, "subtopic" => "Newton's second law", "parent" => "mechanics", "name" => "subtopic 1.1.3"],
["topicid" => 60, "subtopic" => "Wave equation", "parent" => "waves", "name" => "subtopic 1.2.1"],
["topicid" => 59, "subtopic" => "Stoke's law", "parent" => "materials", "name" => "subtopic 1.2.2"],
["topicid" => 59, "subtopic" => "Terminal velocity", "parent" => "materials", "name" => "subtopic 1.2.3"],
["topicid" => 59, "subtopic" => "Young Modulus", "parent" => "materials", "name" => "subtopic 1.3.1"],
["topicid" => 60, "subtopic" => "Standing waves", "parent" => "waves", "name" => "subtopic 1.3.2"],
["topicid" => 60, "subtopic" => "Refractive index", "parent" => "waves", "name" => "subtopic 1.3.3"],
["topicid" => 61, "subtopic" => "Current", "parent" => "dcelectricity", "name" => "subtopic 2.1.1"],
["topicid" => 61, "subtopic" => "Voltage", "parent" => "dcelectricity", "name" => "subtopic 2.1.2"],
["topicid" => 61, "subtopic" => "Resistance", "parent" => "dcelectricity", "name" => "subtopic 2.1.3"],
["topicid" => 62, "subtopic" => "Momentum", "parent" => "furthermechanics", "name" => "subtopic 2.2.1"],
["topicid" => 62, "subtopic" => "Kinetic energy", "parent" => "furthermechanics", "name" => "subtopic 2.2.2"],
["topicid" => 62, "subtopic" => "Potential energy", "parent" => "furthermechanics", "name" => "subtopic 2.2.3"],
["topicid" => 63, "subtopic" => "Electric force", "parent" => "electricfields", "name" => "subtopic 2.3.1"],
["topicid" => 63, "subtopic" => "Electric field lines", "parent" => "electricfields", "name" => "subtopic 2.3.2"],
["topicid" => 63, "subtopic" => "Capacity", "parent" => "electricfields", "name" => "subtopic 2.3.3"],
["topicid" => 64, "subtopic" => "Magnetic field lines", "parent" => "magneticfields", "name" => "subtopic 3.1.1"],
["topicid" => 64, "subtopic" => "Fleming's left hand rule", "parent" => "magneticfields", "name" => "subtopic 3.1.1"],
["topicid" => 64, "subtopic" => "Electromagnetic induction", "parent" => "magneticfields", "name" => "subtopic 3.1.1"]
];
$pageTitle = "Test page";
$action = "index.php";
$unit = "3";
$topic = "Electric fields";
$subtopic = "Electric field lines";
$id = "hidden id";
$button = 'Submit';
// =============================
// End of added PHP support code
// =============================
?>
<link type="text/css" rel="stylesheet" href="/artgibney/css/stylesheet.css" />
<meta charset="utf-8" />
<title><?php htmlout($pageTitle); ?></title>
<link rel="icon" type="image/x-icon" href="http://www.artgibney.com/artgibney/images/favicon.ico" />
</head>
<body>
<div class="formpts">
<div>
<p><a href="..">physCMS home</a> ⇒ <a href="/artgibney/admin/teachingpoints/">Teaching Points</a></p>
<h1><?php htmlout($pageTitle); ?></h1>
</div>
<form action="<?php htmlout($action);?>" method="post">
<div>
<table>
<tr>
<th>Unit</th>
</tr>
<tr>
<td><select name="unit" id="unit" style="background-color:transparent">
<?php for ($i = 1; $i < count($units); $i += 1) {
$selected = ($unit == $i) ? 'selected' : '';
echo "<option value='$i' $selected>{$units[$i]}</option>";
} ?>
</select></td>
</tr>
</table>
</div><br />
<div>
<table>
<tr>
<th>Topic</th>
<th>Sub-topic</th>
</tr>
<tr>
<td><select name="topic" id="topic" style="background-color:transparent">
<?php
foreach($topics as $topical): // unit as the unit above.
if ($topical['unit'] === $unit) {
$value = $topical['topic'];
$selected = ($topic == $value) ? 'selected' : '';
if (!empty($selected)) {
$subtopicId = $topical['id'];
}
echo "<option value='$value' $selected>$value</option>";
}
endforeach; ?>
</select></td>
<td><select name="subtopic" id="subtopic" style="background-color:transparent">
<?php foreach($subtopics as $subtopical):
if ($subtopical['topicid'] == $subtopicId) {
$value = $subtopical['subtopic'];
$selected = ($subtopic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
}
endforeach; ?>
</select></td>
</tr>
</table><br />
<input type="hidden" name="id" value="<?php htmlout($id); ?>" /> <input type="submit" value="<?php htmlout($button); ?>" />
</div>
</form>
</div>
<script>
var newUnits = <?php echo json_encode($units, JSON_PRETTY_PRINT) ?>;
var newTopics = <?php echo json_encode($topics, JSON_PRETTY_PRINT) ?>;
var newSubTopics = <?php echo json_encode($subtopics, JSON_PRETTY_PRINT) ?>;
function getTopicId(name) {
return newTopics.reduce(function (options, option) {
if (option.topic === name) {
options = option.id;
}
return options;
}, 0);
}
function getTopicOptions(topicDB, unitId) {
return topicDB.reduce(function (options, option) {
if (option.unit === unitId) {
options.push(option.topic);
}
return options;
}, []);
}
function getSubtopicOptions(subtopicDB, topicId) {
return subtopicDB.reduce(function (options, option) {
if (option.topicid === topicId) {
options.push(option.subtopic);
}
return options;
}, []);
}
function updateOptions(target, options) {
target.innerHTML = "";
options.forEach(function (option) {
var newOption = document.createElement("option");
newOption.value = option;
newOption.innerHTML = option;
target.options.add(newOption);
});
}
function updateTopic(topic, topicDB, unitId) {
var optionArray = getTopicOptions(topicDB, unitId);
updateOptions(topic, optionArray);
}
function updateSubtopic(subtopic, subtopicDB, topicId) {
var optionArray = getSubtopicOptions(subtopicDB, topicId);
updateOptions(subtopic, optionArray);
}
// event handlers
function topicHandler(evt) {
var topic = this,
subtopic = document.getElementById('subtopic'),
topicId = getTopicId(topic.value);
updateSubtopic(subtopic, newSubTopics, topicId);
};
function unitHandler() {
var unit = this,
topic = document.getElementById('topic'),
unitId = unit.value;
updateTopic(topic, newTopics, unitId);
document.getElementById('topic').onchange();
};
document.getElementById('topic').onchange = topicHandler;
document.getElementById('unit').onchange = unitHandler;
</script>
</body>
</html>
Oh yes, and you may want to consider checking for spelling issues in your database, before students bring you up on them. âCapacityâ is spelled with only one P, for example.
Hi,
Thanks for doing all this.
There is quite a lot of syntax that I have yet to learn, but I will.
The three menus themselves are working perfectly, so that is brilliant!
I have put the other elements in and for some reason the data doesnât insert into the database. Unfortunately I didnât check this before I put the elements back in. So I donât know if I am missing something there or not.
Is it correct that putting input elements in between the unit and topic menus should have no effect on your code. What I mean is, the elements effected by your code are selected by id, not by being next to each other in the DOM?
Here is the full html page,
<?php include_once $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/helpers.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildtopics.inc.php';
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/buildsubtopics.inc.php'; ?>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php $units = range(0, 8);
/*$topics = [
["id" => 60, "topic" => "Waves", "unit" => "2"],
["id" => 57, "topic" => "Mechanics", "unit" => "1"],
["id" => 62, "topic" => "Further mechanics", "unit" => "3"],
["id" => 61, "topic" => "DC Electricity", "unit" => "2"],
["id" => 59, "topic" => "Materials", "unit" => "1"],
["id" => 63, "topic" => "Electric fields", "unit" => "3"],
["id" => 64, "topic" => "Magnetic fields", "unit" => "3"]
];
$subtopics = [
["topicid" => 57, "subtopic" => "Equations of motion", "parent" => "mechanics", "name" => "subtopic 1.1.1"],
["topicid" => 57, "subtopic" => "Vectors", "parent" => "mechanics", "name" => "subtopic 1.1.2"],
["topicid" => 57, "subtopic" => "Newton's second law", "parent" => "mechanics", "name" => "subtopic 1.1.3"],
["topicid" => 60, "subtopic" => "Wave equation", "parent" => "waves", "name" => "subtopic 1.2.1"],
["topicid" => 59, "subtopic" => "Stoke's law", "parent" => "materials", "name" => "subtopic 1.2.2"],
["topicid" => 59, "subtopic" => "Terminal velocity", "parent" => "materials", "name" => "subtopic 1.2.3"],
["topicid" => 59, "subtopic" => "Young Modulus", "parent" => "materials", "name" => "subtopic 1.3.1"],
["topicid" => 60, "subtopic" => "Standing waves", "parent" => "waves", "name" => "subtopic 1.3.2"],
["topicid" => 60, "subtopic" => "Refractive index", "parent" => "waves", "name" => "subtopic 1.3.3"],
["topicid" => 61, "subtopic" => "Current", "parent" => "dcelectricity", "name" => "subtopic 2.1.1"],
["topicid" => 61, "subtopic" => "Voltage", "parent" => "dcelectricity", "name" => "subtopic 2.1.2"],
["topicid" => 61, "subtopic" => "Resistance", "parent" => "dcelectricity", "name" => "subtopic 2.1.3"],
["topicid" => 62, "subtopic" => "Momentum", "parent" => "furthermechanics", "name" => "subtopic 2.2.1"],
["topicid" => 62, "subtopic" => "Kinetic energy", "parent" => "furthermechanics", "name" => "subtopic 2.2.2"],
["topicid" => 62, "subtopic" => "Potential energy", "parent" => "furthermechanics", "name" => "subtopic 2.2.3"],
["topicid" => 63, "subtopic" => "Electric force", "parent" => "electricfields", "name" => "subtopic 2.3.1"],
["topicid" => 63, "subtopic" => "Electric field lines", "parent" => "electricfields", "name" => "subtopic 2.3.2"],
["topicid" => 63, "subtopic" => "Capacity", "parent" => "electricfields", "name" => "subtopic 2.3.3"],
["topicid" => 64, "subtopic" => "Magnetic field lines", "parent" => "magneticfields", "name" => "subtopic 3.1.1"],
["topicid" => 64, "subtopic" => "Fleming's left hand rule", "parent" => "magneticfields", "name" => "subtopic 3.1.1"],
["topicid" => 64, "subtopic" => "Electromagnetic induction", "parent" => "magneticfields", "name" => "subtopic 3.1.1"]
];
$pageTitle = "Test page";
$action = "index.php";
$unit = "3";
$topic = "Electric fields";
$subtopic = "Electric field lines";
$id = "hidden id";
$button = 'Submit';*/
?>
<link type="text/css" rel="stylesheet" href="/artgibney/css/stylesheet.css" />
<meta charset="utf-8" />
<title><?php htmlout($pageTitle); ?></title>
<link rel="icon" type="image/x-icon" href="http://www.artgibney.com/artgibney/images/favicon.ico" />
</head>
<body>
<div class="formpts">
<div>
<p><a href="..">physCMS home</a> ⇒ <a href="/artgibney/admin/teachingpoints/">Teaching Points</a></p>
<h1><?php htmlout($pageTitle); ?></h1>
</div>
<form action="<?php htmlout($action);?>" method="post">
<div>
<table>
<tr>
<th>Unit</th>
<th>Year</th>
<th>Exam</th>
<th>Question</th>
<th>Part</th>
<th>Sub-part</th>
<th>Question type</th>
</tr>
<tr>
<td><select name="unit" id="unit" style="background-color:transparent">
<?php for ($i = 1; $i < count($units); $i += 1) {
$selected = ($unit == $i) ? 'selected' : '';
echo "<option value='$i' $selected>{$units[$i]}</option>";
} ?>
</select></td>
<?php $years = ['2009' => '2009', '2010' => '2010', '2011' => '2011', '2012' => '2012', '2013' => '2013', '2014' => '2014', '2015' => '2015'];
//$years = range(2009, 2015);
?>
<td>
<select name="year" id="year" style="background-color:transparent">
<?php foreach($years as $key=>$option):
$selected = ($year == $key) ? 'selected' : '';
echo "<option value='$key' $selected>$option</option>";
echo($key);
endforeach; ?>
</select>
</td>
<?php $exams = ['January' => 'January', 'June' => 'June']; ?>
<td>
<select name="exam" id="exam" style="background-color:transparent">
<?php foreach($exams as $key=>$option):
$selected = ($exam == $key) ? 'selected' : '';
echo "<option value='$key' $selected>$option</option>";
endforeach; ?>
</select>
</td>
<td>
<input type="number" name="question" min="1" max="25" value="<?php htmlout($question);?>">
</td>
<td>
<input type="text" name="part" value="<?php htmlout($part);?>">
</td>
<td>
<input type="text" name="subpart" value="<?php htmlout($subpart);?>">
</td>
<?php
$questions = ['Multiple Choice' => 'Multiple Choice', 'Calculation' => 'Calculation', 'Short answer' => 'Short answer', 'Long answer' => 'Long answer'];
?>
<td>
<select name="questiontype" id="questiontype" style="background-color:transparent">
<?php foreach($questions as $key=>$option):
$selected = ($questiontype == $key) ? 'selected' : '';
echo "<option value='$key' $selected>$option</option>";
endforeach; ?>
</select>
</td>
</tr>
</table>
</div>
<br/>
<div>
<table>
<tr>
<th>Topic</th>
<th>Sub-topic</th>
<th>Teaching point</th>
</tr>
<tr>
<td><select name="topic" id="topic" style="background-color:transparent">
<?php
foreach($topics as $topical): // unit as the unit above.
if ($topical['unit'] === $unit) {
$value = $topical['topic'];
$selected = ($topic == $value) ? 'selected' : '';
if (!empty($selected)) {
$subtopicId = $topical['id'];
}
echo "<option value='$value' $selected>$value</option>";
}
endforeach; ?>
</select></td>
<td><select name="subtopic" id="subtopic" style="background-color:transparent">
<?php foreach($subtopics as $subtopical):
if ($subtopical['topicid'] == $subtopicId) {
$value = $subtopical['subtopic'];
$selected = ($subtopic == $value) ? 'selected' : '';
echo "<option value='$value' $selected>$value</option>";
}
endforeach; ?>
</select></td>
<td>
<textarea id="tp" name="tp" rows="3" cols="40"><?php htmlout($tp); ?></textarea>
</td>
</tr>
</table><br />
<input type="hidden" name="id" value="<?php htmlout($id); ?>" /> <input type="submit" value="<?php htmlout($button); ?>" />
</div>
</form>
</div>
<script>
var newUnits = <?php echo json_encode($units, JSON_PRETTY_PRINT) ?>;
var newTopics = <?php echo json_encode($topics, JSON_PRETTY_PRINT) ?>;
var newSubTopics = <?php echo json_encode($subtopics, JSON_PRETTY_PRINT) ?>;
function getTopicId(name) {
return newTopics.reduce(function (options, option) {
if (option.topic === name) {
options = option.id;
}
return options;
}, 0);
}
function getTopicOptions(topicDB, unitId) {
return topicDB.reduce(function (options, option) {
if (option.unit === unitId) {
options.push(option.topic);
}
return options;
}, []);
}
function getSubtopicOptions(subtopicDB, topicId) {
return subtopicDB.reduce(function (options, option) {
if (option.topicid === topicId) {
options.push(option.subtopic);
}
return options;
}, []);
}
function updateOptions(target, options) {
target.innerHTML = "";
options.forEach(function (option) {
var newOption = document.createElement("option");
newOption.value = option;
newOption.innerHTML = option;
target.options.add(newOption);
});
}
function updateTopic(topic, topicDB, unitId) {
var optionArray = getTopicOptions(topicDB, unitId);
updateOptions(topic, optionArray);
}
function updateSubtopic(subtopic, subtopicDB, topicId) {
var optionArray = getSubtopicOptions(subtopicDB, topicId);
updateOptions(subtopic, optionArray);
}
// event handlers
function topicHandler(evt) {
var topic = this,
subtopic = document.getElementById('subtopic'),
topicId = getTopicId(topic.value);
updateSubtopic(subtopic, newSubTopics, topicId);
};
function unitHandler() {
var unit = this,
topic = document.getElementById('topic'),
unitId = unit.value;
updateTopic(topic, newTopics, unitId);
document.getElementById('topic').onchange();
};
document.getElementById('topic').onchange = topicHandler;
document.getElementById('unit').onchange = unitHandler;
</script>
</body>
</html>
An excerpt from the index.php file
if (isset($_GET['add']))
{
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/db.inc.php';
if(countRows('teachingpts')){ //if teachingpts is not empty, else it is being filled for the first time
try
{
$sql = 'SELECT * FROM teachingpts ORDER BY id DESC LIMIT 1';
$s = $pdo->prepare($sql);
$s->execute();
}
catch (PDOException $e)
{
$error = 'Error fetching teachingpoints row details.';
include 'error.html.php';
exit();
}
$row = $s->fetch();
$pageTitle = 'New teaching points';
$action = 'addform';
//$id = $row['id'];//id is not needed as it is auto-incremented by the db
$unit = $row['unit'];
$year = $row['year'];
$exam = $row['exam'];
$question = $row['question'];
$part = $row['part'];
$subpart = $row['subpart'];
$topic = $row['topic'];
$subtopic = $row['subtopic'];
$tp = '';//will leave teaching point blank always as it will always be different
$questiontype = $row['questiontype'];
$button = 'Add teaching points';
} else {
$pageTitle = 'New Teaching Points';
$action = 'addform';
$id = '';
$unit = '';
$year = '';
$exam = '';
$question = '';
$part = '';
$subpart = '';
$topic = '';
$subtopic = '';
$tp = '';
$questiontype = '';
$button = 'Add teaching points';
}
include 'form.html.php';
exit();
}
if (isset($_GET['addform']))
{
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/db.inc.php';
try
{
$sql = 'INSERT INTO teachingpts SET id = :id, unit = :unit, year = :year, exam = :exam, question = :question, part = :part, subpart = :subpart, topic = :topic, subtopic = :subtopic, tp = :tp, questiontype = :questiontype';
$s = $pdo->prepare($sql);
$s->bindParam(':id', $_POST['id']);
$s->bindParam(':unit', $_POST['unit']);
$s->bindParam(':year', $_POST['year']);
$s->bindParam(':exam', $_POST['exam']);
$s->bindParam(':question', $_POST['question']);
$s->bindParam(':part', $_POST['part']);
$s->bindParam(':subpart', $_POST['subpart']);
$s->bindParam(':topic', $_POST['topic']);
$s->bindParam(':subtopic', $_POST['subtopic']);
$s->bindParam(':tp', $_POST['tp']);
$s->bindParam(':questiontype', $_POST['questiontype']);
$s->execute();
}
catch (PDOExceptin $e)
{
$error = 'Error adding submitted teachpoints table data.';
include 'error.html.php';
exit();
}
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/db.inc.php';
try
{
$result = $pdo->query('SELECT id, unit, year, exam, question, part, subpart, topic, subtopic, tp, questiontype FROM teachingpts');
}
catch (PDOException $e)
{
$error = 'Error fetching data from teachingpoints table.';
include 'error.html.php';
exit();
}
foreach ($result as $row)
{
$teachingpts[] = array('id' => $row['id'], 'unit' => $row['unit'], 'year' => $row['year'], 'exam' => $row['exam'], 'question' => $row['question'], 'part' => $row['part'], 'subpart' => $row['subpart'], 'topic' => $row['topic'], 'subtopic' => $row['subtopic'], 'tp' => $row['tp'], 'questiontype' => $row['questiontype']);
}
header('Location: .');
exit();
}
I have looked through it carefully but just canât see why the data isnât sending to the database.
You really did tidy up the code a lot. i keep seeing new little things like,
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
I am not worried yet about the spelling of topics and subtopics as I still have a lot of work to do on the form that I use to input that information. The arrays for topics and subtopics here are just a sample.
It is difficult to get time during the week. Canât wait for the weekend when I can really look at your code properly.
Thanks again for all your help,
Shane
The form action is set to be âaddformâ. Does that page exist? Normally a suffix is used for that type of page, such as âaddform.phpâ.
Thanks - thatâs from an online Html Tidy that I used to rapidly clean up a variety of issues.
If I were to clean things up further though things could become distracting. Things such as moving style attributes in to the style sheet, and the script code out to a separate file.
And Iâd want to do something about the nearly identical code in the getTopicOptions(), getSubtopicOptions(), updateTopic(), and updateSubtopic() functions.
Hi,
âaddformâ is just an empty variable which I send to index.php to active the code in the if loop,
if (isset($_GET['addform']))
{
include $_SERVER['DOCUMENT_ROOT'] . '/artgibney/includes/db.inc.php';
try
{ ...........................
which should insert the data into the database.
Thanks for the link to HTML Tidy, very useful, I never knew about this.
Thanks,
Shane
I had an errors. Should have had a ? before the action = string
<form action="?<?php htmlout($action);?>" method="post">
Thank you so much for all your help,
Shane
Glad to see that you figured that out.
Youâre welcome. It wonât fix all sins, but can be helpful to get a majority of HTML syntax issues resolved.
I find that jslint.com is very handy for warning about potential problems in JavaScript. For example, the scripting code that I did isnât immune from issues either. Before passing the code through there though,
Iâll first pass it through beautify JavaScript to get the worst of the code indenting issues sorted out. Then weâre ready for the jslint pass.
About the only issues are that the newUnits array and the evt variable arenât used, and a semicolon that shouldnât have been there was at the end of the function block.
Other than that only a few standard parts were needed, such as to tell the linter that weâre using a browser, and to wrap the code in an immediately-invoked function expression to help protect the global namespace from our functions
/*jslint browser: true */
(function () {
"use strict";
// code in here
}());
The âuse strictâ ensures that certain common code conventions are followed, throws useful errors when bad things happen, and disables certain bad parts of JavaScript.
Hereâs the full script that now passes jslint.com even on the harshest of settings:
(function () {
"use strict";
var newTopics = <?php echo json_encode($topics, JSON_PRETTY_PRINT) ?>,
newSubTopics = <?php echo json_encode($subtopics, JSON_PRETTY_PRINT) ?>;
function getTopicId(name) {
return newTopics.reduce(function (options, option) {
if (option.topic === name) {
options = option.id;
}
return options;
}, 0);
}
function getTopicOptions(topicDB, unitId) {
return topicDB.reduce(function (options, option) {
if (option.unit === unitId) {
options.push(option.topic);
}
return options;
}, []);
}
function getSubtopicOptions(subtopicDB, topicId) {
return subtopicDB.reduce(function (options, option) {
if (option.topicid === topicId) {
options.push(option.subtopic);
}
return options;
}, []);
}
function updateOptions(target, options) {
target.innerHTML = "";
options.forEach(function (option) {
var newOption = document.createElement("option");
newOption.value = option;
newOption.innerHTML = option;
target.options.add(newOption);
});
}
function updateTopic(topic, topicDB, unitId) {
var optionArray = getTopicOptions(topicDB, unitId);
updateOptions(topic, optionArray);
}
function updateSubtopic(subtopic, subtopicDB, topicId) {
var optionArray = getSubtopicOptions(subtopicDB, topicId);
updateOptions(subtopic, optionArray);
}
// event handlers
function topicHandler() {
var topic = this,
subtopic = document.getElementById('subtopic'),
topicId = getTopicId(topic.value);
updateSubtopic(subtopic, newSubTopics, topicId);
}
function unitHandler() {
var unit = this,
topic = document.getElementById('topic'),
unitId = unit.value;
updateTopic(topic, newTopics, unitId);
document.getElementById('topic').onchange();
}
document.getElementById('topic').onchange = topicHandler;
document.getElementById('unit').onchange = unitHandler;
}());
This comment echoes of the preface to âJavascript the good partsâ which Iâve been browsing. He also talks about jslint the JS parser.
Thanks for these great links. It really is a fantastic language for the web and canât wait to learn more i.e. understand your code
Also i am glad you mentioned the evt is not used because I was looking at that for a while trying to figure it out.
Thanks,
Shane