JSFiddle - React, Tailwind, and code Playground

by Gwyn Milcote

HTML

<div class='guide-demo' id='guide-demo-agouti'></div>
<hr>
<div class='guide-demo' id='guide-demo-brown'></div>
<hr>
<div class='guide-demo' id='guide-demo-pied'></div>
<hr>
<div class='guide-demo' id='guide-demo-pangare'></div>

CSS

.guide-demo {
    border: 1px solid silver;
    padding: 10px;
    margin: 20px auto;
    text-align: center;
}

.guide-demo .demo-image {
    border: 1px solid green;
    margin: 10px auto;
    width: 300px;
    height: 100px;
}
.guide-demo .demo-desc {
    margin: 10px auto;
}
.guide-demo .demo-controls .switch {
    cursor: pointer;
    border: 1px solid grey;
    padding: 3px 5px;
    border-radius: 3px;
    margin: 0px 5px;
}

.guide-demo .selected-trait {
    background-color: gold;
}

/* Range demos */

.demo-controls .slider {
    width: 300px;
    height: 20px;
    display: inline-block;
}
.demo-controls .change {
    width: 40px;
    height: 40px;
    cursor: pointer;
    border: 1px solid silver;
    background-color: #eee;
    font-size: 36px;
    display: inline-block;
}

input[type=range]:focus {
  outline: none;
}

JavaScript

const imagePath = "http://milcote.net/images/genetics/";

function capitalise(str){
    return str.charAt(0).toUpperCase() + str.slice(1);
}

class Demo {

    constructor(animal){
	    this.path = imagePath + animal + "/demo/";
        this.rangeValues = {};
	}
    
    setSwitchDemos(obj){
        this.switchDemos = obj;
        for(let demoName of Object.keys(obj)){
		    this.buildSwitchDemo(demoName);
		}
    }
    setRangeDemos(obj){
        this.rangeDemos = obj;
        for(let demoName of Object.keys(obj)){
		    this.buildRangeDemo(demoName);
            // Start them all as 'wildtype'.
            this.rangeValues[demoName] = 0;
		}
    }
	
	// First the switch-type demos.
    // Click buttons to flip between images/descs.
    
	buildSwitchDemo(demoName){
        let html = "Comparison: click buttons to see effects of <b>" + capitalise(demoName) + "</b> alleles.";
		html += "<div class='demo-image' id='demo-image-" + demoName + "'>" + this.path + "wt.png" + "</div>";
		html += "<div class='demo-desc' id='demo-desc-" + demoName + "'>Wildtype</div>";
		html += "<div class='demo-controls'>";
		for(let trait of this.switchDemos[demoName]){
		    html += "<button class='switch' data-demo='" + demoName + "' data-trait='" + trait[0] + "'>" + trait[1] + "</button>";
		}
		html += "</div>";
        $("#guide-demo-" + demoName).html(html);
	}

    // Display the new image and description.
    switchImage(demoName, traitKey){
	    // Find the option that was clicked.
		let t = false;
		for(let arr of this.switchDemos[demoName]){
		    if(arr[0] == traitKey){
			    t = arr;
			}
		}
		if(!t){
		    throw "Demo '" + demoName + "': trait '" + traitKey + "' not found.";
		}
        let url = this.path + "wt.png";
        if(traitKey != "wt"){
            url = this.path + demoName + "_" + traitKey + ".png";
        }
	    let desc = "<b>" + t[1] + "</b><br>" + t[2];
		//$("#demo-image-" + demoName).html("<img src='" + url + "'>");		
        $("#demo-image-" +...