An expert system emulates the knowledge and expertise of a human being by breaking it up to two parts: A knowledge base, and an inference engine. First developed in the 1960’s and later became widely used in the 1980’s, it is one of the first successful application of AI.
In this series, we will create a program in JavaScript that will put you into the seat of an animal expert: allowing us to build a knowledge base of animal traits. Once that is built, the program will use the knowledge base to guess which animal most likely fits with those traits. If you want to skip ahead to what we will be making, check out the demo below.

Getting Started
The goal of this part is to create a way for users to interact with the expert system. This will be accomplished through a series of dynamically generated dialog boxes containing form elements such as textboxes, checkboxes, and buttons, created using JavaScript.
To get started let’s create a template for getting a user to interact with the Expert System. Create a new HTML file, using the code below as a template. You can run the html file in your favorite browser.
<html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Expert System Part 1</title> <style> /* Insert CSS Style here*/ </style></head><body> <script> // Inset JavaScript Here </script></body></html>
CSS
First thing we’ll do is use some CSS to create a style for our dialogue boxes. What we have below is something we generated with AI using prompts like blue buttons with rounded corners, grey backgrounds, scalable, and centered on the screen. Put the code below in between the <style> tags.
* { margin: 0; padding: 0; box-sizing: border-box;}body { display: flex; justify-content: center; align-items: center; min-height: 100vh; background-color: #424242; font-family: Arial, sans-serif;}.form-container { background-color: white; padding: 30px; border-radius: 12px; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); max-width: 500px; width: 100%;}.form-container h2 { margin-bottom: 20px; color: #333;}.form-group { margin-bottom: 20px;}label { display: block; margin-bottom: 8px; color: #555; font-weight: bold; white-space: pre-line;}input[type="text"],textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-family: Arial, sans-serif;}textarea { width: 100%; padding: 10px; border: 1px solid #ccc; border-radius: 6px; font-family: Arial, sans-serif; resize: vertical;}select { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-family: Arial, sans-serif; background-color: white;}.checkbox-list { display: flex; flex-direction: column; gap: 20px; padding-top: 20px;}.checkbox-item { display: flex; align-items: center;}input[type="checkbox"] { width: 20px; height: 20px; margin-right: 10px; cursor: pointer;}.checkbox-item label { margin: 0; font-weight: normal; cursor: pointer;}button { padding: 12px 24px; margin-right: 10px; margin-top: 20px; border: none; border-radius: 6px; font-size: 16px; cursor: pointer;; transition: background-color 0.3s;}.btn-yes { background-color: #007bff; color: white;}.btn-yes:hover { background-color: #0056b3;}.btn-no { background-color: #6c757d; color: white;}.btn-no:hover { background-color: #545b62;}
We wrap all of it in a form container using some HTML which we will later manipulate using JavaScript. Put the following HTML tags just above the <script> tags.
<div class="form-container" id="formContainer"></div>
Dynamic Forms using JS
To Dynamically create the dialogue boxes in the form container, we’ll use some JavaScript. We’ll call this class DynamicForm and it will be an object under the trf namespace. The template for what we’re doing is as follows:
/* Initialize TRF (The Robot Fish) namespace */this.trf = this.trf||{};//##############################################################################// DynamicForm.js//##############################################################################(function() { "use strict"; function DynamicForm(elementId) { this.container = document.getElementById(elementId); }; var p = DynamicForm.prototype; p.init = function() { //Initialize and clear the form }; p.create = function(config) { // Dynamically create the form using the config }; trf.DynamicForm = DynamicForm;}());
For the constructor we simply pass in the elementId so we can reference it as the container later. We start with implementing the init function below.
p.init = function() { this.textboxes = new Map(); this.checkboxes = new Map(); this.selects = new Map(); while (this.container.firstChild) { this.container.removeChild(this.container.firstChild); }};
The purpose of the init function is to clear the form and get it ready to be populated. New Map objects are created to keep track of the form items for later referencing. To clear any items that might remain in the container, we loop through all the children and remove them. Copy the code below for the create function.
p.create = function(config) { this.init(); // Dynamically create the form using the config const form = document.createElement('form'); const title = document.createElement('h2'); title.textContent = config.title; form.appendChild(title); // Create form fields for (const field of config.fields) { const group = document.createElement('div'); group.className = 'form-group'; if (field.type === 'label') { const label = document.createElement('label'); label.textContent = field.text; group.appendChild(label); }; if (field.type === 'textbox') { const input = document.createElement('input'); input.type = 'text'; input.placeholder = field.placeholder || ''; input.name = field.name || 'input' input.addEventListener('keydown', function(event) { if (event.key === 'Enter') { event.preventDefault();}}); group.appendChild(input); this.textboxes.set(input.name, input); }; if (field.type === 'textarea') { const input = document.createElement('textarea'); input.placeholder = field.placeholder || ''; input.name = field.name || 'input' input.rows = 5; group.appendChild(input); this.textboxes.set(input.name, input); }; if (field.type === 'checkboxList') { const checkboxContainer = document.createElement('div'); checkboxContainer.className = 'checkbox-list'; for (const option of field.options) { const checkboxItem = document.createElement('div'); checkboxItem.className = 'checkbox-item'; const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.value = option; const optionLabel = document.createElement('label'); optionLabel.textContent = option; checkboxItem.appendChild(checkbox); checkboxItem.appendChild(optionLabel); checkboxContainer.appendChild(checkboxItem); this.checkboxes.set(checkbox.value,checkbox); }; group.appendChild(checkboxContainer); }; if (field.type === 'select') { const select = document.createElement('select'); select.name = field.name || 'select'; for (const option of field.options) { const opt = document.createElement('option'); opt.value = option; opt.textContent = option.slice(4); select.appendChild(opt); }; group.appendChild(select); this.selects.set(select.name, select); }; if (field.type === 'buttons') { for (const option of field.options) { const button = document.createElement('button'); button.type = 'button'; button.className = 'btn-' + option.type; button.textContent = option.text; if (option.callback) { button.addEventListener('click', option.callback); } group.appendChild(button); }; }; form.appendChild(group); }; this.container.appendChild(form);};
A couple of things to note for the code above. First, is the use of the init function we created to clear the form. This clears the form at the beginning of each create call.
this.init();
After that, we create the form element and the first item we add to it is always a tittle.
const form = document.createElement('form'); const title = document.createElement('h2');title.textContent = config.title;form.appendChild(title);
Then we loop through all the fields in the config to create the form items like checkboxes, buttons, and labels. We append them to a group, which then gets appended to the form, which then get appended to the container. The example below is what it looks like if we only do this for labels.
for (const field of config.fields) { const group = document.createElement('div'); group.className = 'form-group'; if (field.type === 'label') { const label = document.createElement('label'); label.textContent = field.text; group.appendChild(label); }; form.appendChild(group);};this.container.appendChild(form);
For field types like textbox, checkboxList, and select, we have to set it to the map objects that we created in the init function so that their values and settings can be accessed later. The example below sets the textbox to the textboxes map object using the field name as the key.
if (field.type === 'textbox') { const input = document.createElement('input'); input.type = 'text'; input.placeholder = field.placeholder || ''; input.name = field.name || 'input' input.addEventListener('keydown', function(event) { if (event.key === 'Enter') { event.preventDefault();}}); group.appendChild(input); this.textboxes.set(input.name, input);};
Lastly, we want buttons to do a callback when clicked. This is included in the options of each field because we would like to have multiple buttons in a group.
if (field.type === 'buttons') { for (const option of field.options) { const button = document.createElement('button'); button.type = 'button'; button.className = 'btn-' + option.type; button.textContent = option.text; if (option.callback) { button.addEventListener('click', option.callback); } group.appendChild(button); };};
To get a better understanding of how it all works, below is an example of a dialogue box with all of the different kinds of elements in the DynamicForm. Copy and paste it below the DynamicForm class that we just created.
//##############################################################################// Main Program//##############################################################################var dynamicForm = new trf.DynamicForm("formContainer");dynamicForm.create({ title: 'Generic Title', fields: [ { type: 'label', text:'This is a new label'}, { type: 'textbox', placeholder: 'Placeholder Text' , name: 'textbox'}, { type: 'textarea', placeholder: 'Placeholder Text Area' , name: 'textarea'}, { type: 'checkboxList', options: ['Item1', 'Item2', 'Item3', 'Item4', 'Item5'] }, { type: 'buttons', options: [ {type: 'yes', text: 'Yes', callback: handleYes}, {type: 'no', text: 'No', callback: function(){alert('No')}}, {type: 'no', text: 'Cancel', callback: function(){alert('Cancel')}} ]}] }); function handleYes(){ alert( "This is what's in the textbox: " + dynamicForm.textboxes.get('textbox').value + "\n" + "This is what's in the textarea: " + dynamicForm.textboxes.get('textarea').value + "\n" + "This is the state of checkbox Item1: " + dynamicForm.checkboxes.get('Item1').checked );};
This is what you get when you get when you run the code above.

That’s it for all the GUI that we need to make for this project. You can download the complete project file for this part here. In the next parts, we will dive into programming the inference engine and the knowledge base. For now, have fun creating your new dialogue boxes using DynamicForm!







