Click here to Skip to main content
Click here to Skip to main content

Tagged as

How To Make Complicated Multi Line Charts using Simple HTML and JavaScript

, 19 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
How to make complicated “multi line charts” using simple HTML and JavaScript

Introduction

Graphs on websites are an effective way of showing statistical data. And viewers can very easily find the trends.

Now displaying data through plug-ins like Flash and Silverlight can be a very expensive operation. The client needs to have Flash, Silverlight or any other plug-in, and the graph file will be downloaded in client browser that will slow down the process. And complicated code is required in addition to understanding the actual user requirements.

To overcome all the issues and achieving a custom developed graph, HTML and JavaScript enabled Graph is the easiest and most efficient solution.

To demonstrate this, I have taken a very complicated Graph that will have Temperature, Pulse, Respiratory and BP.

  • All vitals in one graph
  • Every vital with different defined ranges
  • Different color codes
  • Date wise and time wise
  • Lines in between the values
  • Values in the point with color codes
  • If the value is zero, it should not show

All the techniques implementation is completed using simple techniques.

Graph

The above graph shows Temperature, Pulse, Respiratory and BP. All in one graph for multiple dates on different times

CSS

<style type="text/css">

        #patientchart
        {
            width: 100%;
            height: 500px;
        }

        .charthead
        {
            position: fixed;
            background-color: #FFF;
            top: 180px;
            z-index: 100 !important;
        }

        .graphline
        {
            padding: 0px;
            margin: 0px;
            line-height: 1px;
            position: absolute;
            z-index: -1 !important;
            opacity: 0.5;
            filter: alpha(opacity=50);
        }

        .chart
        {
            border-left: 1px solid #DDD;
            border-top: 1px solid #DDD;
        }
        .chart td
        {
            font-size: 13px;
            border-right: 1px solid #DDD;
            border-bottom: 1px solid #DDD;
            text-align: center;
            width: 33px !important;
        }

        .pl
        {
            color: #1F7BC6;
        }
        .tl
        {
            color: #C61F1F;
        }
        .rl
        {
            color: #1FC64E;
        }
        .bp
        {
            color: #C69F1F;
        }

        .plh
        {
            background-color: #1F7BC6;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .tlh
        {
            background-color: #C61F1F;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .rlh
        {
            background-color: #1FC64E;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .bph
        {
            background-color: #C69F1F;
            color: #FFF;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }
        .sbph
        {
            background-color: #C69F1F;
            color: #000;
            opacity: 0.8;
            filter: alpha(opacity=80);
        }

        .c02
        {
            border-right-color: #DDD !important;
            border-bottom-color: #DDD !important;
        }
        .c06
        {
            border-right-color: #CCC !important;
            border-bottom-color: #CCC !important;
        }
        .c10
        {
            border-right-color: #BBB !important;
            border-bottom-color: #BBB !important;
        }
        .c14
        {
            border-right-color: #AAA !important;
            border-bottom-color: #AAA !important;
        }
        .c18
        {
            border-right-color: #999 !important;
            border-bottom-color: #999 !important;
        }
        .c22
        {
            border-right-color: #888 !important;
            border-bottom-color: #888 !important;
        }

        #dummyheader
        {
            height: 50px;
        }
    </style>

JavaScript

<script type="text/javascript">

	 // Main function to generating the graph

        function generateChartData() {
            generateReport();

     var listLength = VL.length;

	   // If List is empty do nothing

            if (listLength < 1) {
                return;
            }

	   // to hold the place for scrolling effect cancellation

            var ctbl = '<div id="dummyheader" />';

            ctbl += '<div id="detgr"></div><table class="chart charthead"
            cellspacing="0" id="chartheader"><tr><td colspan="4">DATE</td>';

            var firstDate = new Date(VL[0].date);

            var range = 4;
            var startDate = new Date(VL[listLength - 1].date);
            startDate.setDate(startDate.getDate() - range);
            startDate.setDate(startDate.getDate() + 1);

            if (firstDate > startDate) {
                startDate = firstDate;
            }

            var DATE = '';
            for (i = 0; i < range; i++) {
                var newDate = new Date(startDate);
                newDate.setDate(newDate.getDate() + i);
                DATE = GetFormatedDate(newDate);
                ctbl += '<td colspan="6">' + DATE + '</td>';
            }

            ctbl += '</tr>';
            ctbl += '<tr><td class="tlh">T</td><td class="plh">
            P</td><td class="rlh">R</td><td class="bph">B.P</td>';

	   // Hours
            var HRS = [];
            HRS.push('02');
            HRS.push('06');
            HRS.push('10');
            HRS.push('14');
            HRS.push('18');
            HRS.push('22');
            for (ti = 0; ti < range; ti++) {
                for (hi = 0; hi < HRS.length; hi++) {
                    ctbl += '<td class="c' + HRS[hi] + '">' + HRS[hi] + '</td>';
                }
            }
            ctbl += '</tr></table>';

            ctbl += '<div class="detdiv" id="detd"><table class="chart"
            style="z-index:-2 !important" cellspacing="0">'

            generateRanges();

            var RESVAL = '';
            var idx = 0;

            EPL = [];
            ETL = [];
            ERL = [];
            ESBP = [];
            EDBP = [];

            for (ri = 0; ri <= 45; ri++) {
                ctbl += '<tr><td class="tl">' + (TL[ri].val == 0 ? "" : TL[ri].val) +
                '</td><td class="pl">' + (PL[ri].val == 0 ? "" : PL[ri].val) +
                '</td><td class="rl">' + (RL[ri].val == 0 ? "" : RL[ri].val) +
                '</td><td class="bp">' + (BP[ri].val == 0 ? "" : BP[ri].val) + '</td>';

                for (ii = 0; ii < range; ii++) {
                    var newDate = new Date(startDate);
                    newDate.setDate(newDate.getDate() + ii);

                    for (ihi = 0; ihi < HRS.length; ihi++) {
                        RESVAL = '';
                        for (vi = 0; vi < listLength; vi++) {
                            if (GetFormatedDate(VL[vi].date) == GetFormatedDate(newDate)) {
                                var id = new Date(VL[vi].date);
                                if (id.getHours() == (HRS[ihi] * 1)) {
                                    RESVAL = getVTL(vi, idx);
                                }
                            }
                        }
                        ctbl += '<td class="c' + HRS[ihi] + '">' + RESVAL + '</td>';
                    }
                }
                ctbl += '</tr>';
                idx++;
            }

            ctbl += '</table></div>';
            $('#patientchart').html(ctbl);
            drawGraphs();

	   // to show the header always on top

            $(window).scroll(function () {
                var head = $(window).scrollTop();
                if (head > 180) {
                    $('#chartheader').css('top', '0px');
                } else {
                    head = 175 - head;
                    $('#chartheader').css('top', head + 'px');
                }
            });
        }

// Functions for getting date formats, that handles pretty much every format

        function GetFormatedDate(date) {
            var dt = new Date(date);
            return (dt.getMonth() + 1) + '/' + dt.getDate() + '/' + dt.getFullYear();
        }
        function GetFormatedDateTime(date) {
            var dt = new Date(date);
            return (dt.getMonth() + 1) + '/' + dt.getDate() + '/' + dt.getFullYear() + ' ' + dt.getHours();
        }

// Variables for saving values

        var VL = [];

        var PL = [];
        var TL = [];
        var RL = [];
        var BP = [];

        function generateReport() {

	// List of data can be from database or hidden field

        var list = $('#HF_Vitals').val().split('||');
            VL = [];
            if (list.length < 1) {
                return;
            }

            for (vi = 0; vi < list.length; vi++) {
                var row = list[vi].split('|');

                VL.push({ date: row[0],
                    dbp: Math.round(row[1]),
                    pul: Math.round(row[2]),
                    res: Math.round(row[3]),
                    sbp: Math.round(row[4]),
                    temp: row[5] * 1,
                    counter: Math.round(row[6])
                });
            }
        }

	// here we define the ranges for graph (note these should be defined to view graph in one layout)

        function generateRanges() {
            PL = [];
            TL = [];
            RL = [];
            BP = [];

            PL.push({ ri: 0, val: 0 });
            PL.push({ ri: 1, val: 0 });
            PL.push({ ri: 2, val: 0 });
            PL.push({ ri: 3, val: 150 });
            PL.push({ ri: 4, val: 149 });
            PL.push({ ri: 5, val: 148 });
            PL.push({ ri: 6, val: 145 });
            PL.push({ ri: 7, val: 142 });
            PL.push({ ri: 8, val: 136 });
            PL.push({ ri: 9, val: 133 });
            PL.push({ ri: 10, val: 130 });
            PL.push({ ri: 11, val: 127 });
            PL.push({ ri: 12, val: 124 });
            PL.push({ ri: 13, val: 121 });
            PL.push({ ri: 14, val: 118 });
            PL.push({ ri: 15, val: 115 });
            PL.push({ ri: 16, val: 112 });
            PL.push({ ri: 17, val: 109 });
            PL.push({ ri: 18, val: 106 });
            PL.push({ ri: 19, val: 103 });
            PL.push({ ri: 20, val: 100 });
            PL.push({ ri: 21, val: 97 });
            PL.push({ ri: 22, val: 94 });
            PL.push({ ri: 23, val: 91 });
            PL.push({ ri: 24, val: 88 });
            PL.push({ ri: 25, val: 85 });
            PL.push({ ri: 26, val: 82 });
            PL.push({ ri: 27, val: 79 });
            PL.push({ ri: 28, val: 76 });
            PL.push({ ri: 29, val: 73 });
            PL.push({ ri: 30, val: 70 });
            PL.push({ ri: 31, val: 67 });
            PL.push({ ri: 32, val: 64 });
            PL.push({ ri: 33, val: 61 });
            PL.push({ ri: 34, val: 58 });
            PL.push({ ri: 35, val: 55 });
            PL.push({ ri: 36, val: 49 });
            PL.push({ ri: 37, val: 46 });
            PL.push({ ri: 38, val: 43 });
            PL.push({ ri: 39, val: 40 });
            PL.push({ ri: 40, val: 0 });
            PL.push({ ri: 41, val: 0 });
            PL.push({ ri: 42, val: 0 });
            PL.push({ ri: 43, val: 0 });
            PL.push({ ri: 44, val: 0 });
            PL.push({ ri: 45, val: 0 });

            TL.push({ ri: 0, val: 0 });
            TL.push({ ri: 1, val: 0 });
            TL.push({ ri: 2, val: 0 });
            TL.push({ ri: 3, val: 0 });
            TL.push({ ri: 4, val: 0 });
            TL.push({ ri: 5, val: 0 });
            TL.push({ ri: 6, val: 0 });
            TL.push({ ri: 7, val: 0 });
            TL.push({ ri: 8, val: 0 });
            TL.push({ ri: 9, val: 0 });
            TL.push({ ri: 10, val: 0 });
            TL.push({ ri: 11, val: 0 });
            TL.push({ ri: 12, val: 0 });
            TL.push({ ri: 13, val: 0 });
            TL.push({ ri: 14, val: 0 });
            TL.push({ ri: 15, val: 0 });
            TL.push({ ri: 16, val: 0 });
            TL.push({ ri: 17, val: 0 });
            TL.push({ ri: 18, val: 0 });
            TL.push({ ri: 19, val: 0 });
            TL.push({ ri: 20, val: 0 });
            TL.push({ ri: 21, val: 0 });
            TL.push({ ri: 22, val: 0 });
            TL.push({ ri: 23, val: 0 });
            TL.push({ ri: 24, val: 0 });
            TL.push({ ri: 25, val: 0 });
            TL.push({ ri: 26, val: 0 });
            TL.push({ ri: 27, val: 105.0 });
            TL.push({ ri: 28, val: 104.5 });
            TL.push({ ri: 29, val: 104.0 });
            TL.push({ ri: 30, val: 103.5 });
            TL.push({ ri: 31, val: 103.0 });
            TL.push({ ri: 32, val: 102.5 });
            TL.push({ ri: 33, val: 102.0 });
            TL.push({ ri: 34, val: 101.5 });
            TL.push({ ri: 35, val: 101.0 });
            TL.push({ ri: 36, val: 100.5 });
            TL.push({ ri: 37, val: 100.0 });
            TL.push({ ri: 38, val: 99.5 });
            TL.push({ ri: 39, val: 99.0 });
            TL.push({ ri: 40, val: 98.5 });
            TL.push({ ri: 41, val: 98.0 });
            TL.push({ ri: 42, val: 97.5 });
            TL.push({ ri: 43, val: 97.0 });
            TL.push({ ri: 44, val: 96.5 });
            TL.push({ ri: 45, val: 96.0 });

            RL.push({ ri: 0, val: 40 });
            RL.push({ ri: 1, val: 38 });
            RL.push({ ri: 2, val: 36 });
            RL.push({ ri: 3, val: 34 });
            RL.push({ ri: 4, val: 32 });
            RL.push({ ri: 5, val: 30 });
            RL.push({ ri: 6, val: 28 });
            RL.push({ ri: 7, val: 26 });
            RL.push({ ri: 8, val: 24 });
            RL.push({ ri: 9, val: 22 });
            RL.push({ ri: 10, val: 20 });
            RL.push({ ri: 11, val: 18 });
            RL.push({ ri: 12, val: 16 });
            RL.push({ ri: 13, val: 14 });
            RL.push({ ri: 14, val: 12 });
            RL.push({ ri: 15, val: 0 });
            RL.push({ ri: 16, val: 0 });
            RL.push({ ri: 17, val: 0 });
            RL.push({ ri: 18, val: 0 });
            RL.push({ ri: 19, val: 0 });
            RL.push({ ri: 20, val: 0 });
            RL.push({ ri: 21, val: 0 });
            RL.push({ ri: 22, val: 0 });
            RL.push({ ri: 23, val: 0 });
            RL.push({ ri: 24, val: 0 });
            RL.push({ ri: 25, val: 0 });
            RL.push({ ri: 26, val: 0 });
            RL.push({ ri: 27, val: 0 });
            RL.push({ ri: 28, val: 0 });
            RL.push({ ri: 29, val: 0 });
            RL.push({ ri: 30, val: 0 });
            RL.push({ ri: 31, val: 0 });
            RL.push({ ri: 32, val: 0 });
            RL.push({ ri: 33, val: 0 });
            RL.push({ ri: 34, val: 0 });
            RL.push({ ri: 35, val: 0 });
            RL.push({ ri: 36, val: 0 });
            RL.push({ ri: 37, val: 0 });
            RL.push({ ri: 38, val: 0 });
            RL.push({ ri: 39, val: 0 });
            RL.push({ ri: 40, val: 0 });
            RL.push({ ri: 41, val: 0 });
            RL.push({ ri: 42, val: 0 });
            RL.push({ ri: 43, val: 0 });
            RL.push({ ri: 44, val: 0 });
            RL.push({ ri: 45, val: 0 });

            BP.push({ ri: 0, val: 250 });
            BP.push({ ri: 1, val: 240 });
            BP.push({ ri: 2, val: 230 });
            BP.push({ ri: 3, val: 220 });
            BP.push({ ri: 4, val: 210 });
            BP.push({ ri: 5, val: 200 });
            BP.push({ ri: 6, val: 190 });
            BP.push({ ri: 7, val: 180 });
            BP.push({ ri: 8, val: 170 });
            BP.push({ ri: 9, val: 160 });
            BP.push({ ri: 10, val: 150 });
            BP.push({ ri: 11, val: 140 });
            BP.push({ ri: 12, val: 130 });
            BP.push({ ri: 13, val: 120 });
            BP.push({ ri: 14, val: 110 });
            BP.push({ ri: 15, val: 100 });
            BP.push({ ri: 16, val: 90 });
            BP.push({ ri: 17, val: 80 });
            BP.push({ ri: 18, val: 70 });
            BP.push({ ri: 19, val: 60 });
            BP.push({ ri: 20, val: 50 });
            BP.push({ ri: 21, val: 40 });
            BP.push({ ri: 22, val: 30 });
            BP.push({ ri: 23, val: 20 });
            BP.push({ ri: 24, val: 0 });
            BP.push({ ri: 25, val: 0 });
            BP.push({ ri: 26, val: 0 });
            BP.push({ ri: 27, val: 0 });
            BP.push({ ri: 28, val: 0 });
            BP.push({ ri: 29, val: 0 });
            BP.push({ ri: 30, val: 0 });
            BP.push({ ri: 31, val: 0 });
            BP.push({ ri: 32, val: 0 });
            BP.push({ ri: 33, val: 0 });
            BP.push({ ri: 34, val: 0 });
            BP.push({ ri: 35, val: 0 });
            BP.push({ ri: 36, val: 0 });
            BP.push({ ri: 37, val: 0 });
            BP.push({ ri: 38, val: 0 });
            BP.push({ ri: 39, val: 0 });
            BP.push({ ri: 40, val: 0 });
            BP.push({ ri: 41, val: 0 });
            BP.push({ ri: 42, val: 0 });
            BP.push({ ri: 43, val: 0 });
            BP.push({ ri: 44, val: 0 });
            BP.push({ ri: 45, val: 0 });
        }

	// arrays to be used for lines

        var EPL = [];
        var ETL = [];
        var ERL = [];
        var ESBP = [];
        var EDBP = [];

	// add points in graph and put results in arrays (so that lines can be drawn between them)

        function getVTL(vi, idx) {

            var resval = '';
            var vt = VL[vi];
            var did = vt.counter;
            if (vt.dbp > 0) {
                if (BP[idx].val == VL[vi].dbp) {
                    resval += '<div class="bph" id="bp_' + did + '">' + (vt.dbp * 1) + '</div>';
                    EDBP.push(did);
                }
            }

            if (vt.pul > 0) {
                if (PL[idx].val == vt.pul) {
                    resval += '<div class="plh" id="pul_' + did + '">' + vt.pul + '</div>';
                    EPL.push(did);
                }
            }

            if (VL[vi].res > 0) {
                if (RL[idx].val == vt.res) {
                    resval += '<div class="rlh" id="res_' + did + '">' + vt.res + '</div>';
                    ERL.push(did);
                }
            }

            if (vt.sbp > 0) {
                if (BP[idx].val == vt.sbp) {
                    resval += '<div class="sbph" id="sbp_' + did + '">' + (vt.sbp * 1) + '</div>';
                    ESBP.push(did);
                }
            }

            if (vt.temp > 0) {
                if (TL[idx].val == vt.temp) {
                    resval += '<div class="tlh" id="temp_' + did + '">' + vt.temp + '</div>';
                    ETL.push(did);
                }
            }

            return resval;
        }

	// sort number is used to change nature or sorting for array

        function sortNumber(a, b) {
            return a - b;
        }

        function drawGraphs() {
            var div1 = '';
            var div2 = '';
            var idx = 0;
            var str = '';

	   // Sort Arrays in numeric nature

            EDBP.sort(sortNumber);
            EPL.sort(sortNumber);
            ERL.sort(sortNumber);
            ESBP.sort(sortNumber);
            ETL.sort(sortNumber);

            for (i = 0; i <= EDBP.length + 1; i++) {
                str += EDBP[i] + ',';
                div1 = 'bp_' + EDBP[i];
                div2 = 'bp_' + EDBP[i + 1];
                connect(div1, div2, '#c69f1f', 2);
            }
            for (i = 0; i <= EPL.length + 1; i++) {
                div1 = 'pul_' + EPL[i];
                div2 = 'pul_' + EPL[i + 1];
                connect(div1, div2, '#1f7bc6', 2);
            }

            for (i = 0; i <= ERL.length + 1; i++) {
                div1 = 'res_' + ERL[i];
                div2 = 'res_' + ERL[i + 1];
                connect(div1, div2, '#1fc64e', 2);
            }

            for (i = 0; i <= ESBP.length + 1; i++) {
                div1 = 'sbp_' + ESBP[i];
                div2 = 'sbp_' + ESBP[i + 1];
                connect(div1, div2, '#c69f1f', 2);
            }

            for (i = 0; i < ETL.length + 1; i++) {
                div1 = 'temp_' + ETL[i];
                div2 = 'temp_' + ETL[i + 1];
                connect(div1, div2, '#c61f1f', 2);
            }
        }

	// get the off sets of results

        function getOffset(el) {
            if (el == null) {
                return;
            }
            var _x = 0;
            var _y = 0;
            var _w = el.offsetWidth | 0;
            var _h = el.offsetHeight | 0;
            while (el && !isNaN(el.offsetLeft) && !isNaN(el.offsetTop)) {
                _x += el.offsetLeft - el.scrollLeft;
                _y += el.offsetTop - el.scrollTop;
                el = el.offsetParent;
            }
            return { top: _y, left: _x, width: _w, height: _h };
        }

	// connect two nearest points
	// make a div and rotate it between two points (center of point)

        function connect(d1, d2, color, thickness) {
            var div1 = document.getElementById(d1);
            var div2 = document.getElementById(d2);
            if (div1 == null || div2 == null) {
                return;
            }
            var off1 = getOffset(div1);
            var off2 = getOffset(div2);
            // bottom right
            var x1 = off1.left + (off1.width / 2);
            var y1 = off1.top + (off1.height / 2);
            // top right
            var x2 = off2.left + (off2.width / 2); //  + off2.width;
            var y2 = off2.top + (off2.height / 2);
            // distance
            var length = Math.sqrt(((x2 - x1) * (x2 - x1)) + ((y2 - y1) * (y2 - y1)));
            // center
            var cx = ((x1 + x2) / 2) - (length / 2);
            var cy = ((y1 + y2) / 2) - (thickness / 2);
            // angle
            var angle = Math.atan2((y1 - y2), (x1 - x2)) * (180 / Math.PI);
            // make hr
            var htmlLine = "<div class='graphline' style=' height:" +
            thickness + "px; background-color:" + color + ";  left:" + cx +
            "px; top:" + cy + "px; width:" + length + "px; -moz-transform:rotate
            (" + angle + "deg); -webkit-transform:rotate(" + angle + "deg);
            -o-transform:rotate(" + angle + "deg);
            -ms-transform:rotate(" + angle + "deg); transform:rotate(" + angle + "deg);' />";
            //
            //alert(htmlLine);
            document.body.innerHTML += htmlLine;
        }

    </script>

HTML Code

<input name="HF_Vitals" id="HF_Vitals"
value="Saturday, August 16, 2014 6:00:00 PM | 100.00| 73.00| 18.00|  170.00| 98.50| 0||Sunday, August 17, 2014 10:00:00 AM|90.00|115.00|0|160.00|98.50|1||Sunday, August 17, 2014 6:00:00 PM|80.00|76.00|18.00|120.00|98.50|2||Monday, August 18, 2014 10:00:00 AM|110.00|115.00|0|170.00|98.50|3||Monday, August 18, 2014 6:00:00 PM|70.00|67.00|0|130.00|100.00|4"
type="hidden" />

<input type="button" id="btn_showgraph"
value="Show Graph" onclick="generateChartData()" />

<div id="patientchart"></div>

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

nasir_ml
Team Leader
Pakistan Pakistan
Working in the field of software development from the past 8 years. Specialty in web development and cloud architecture. Mostly market based Applications e.g. Finance, Inventory, POS, Medical Records.

Comments and Discussions

 
Generalthnx..:) PinmemberMember 1102360319-Aug-14 21:49 
GeneralRe: thnx..:) Pinprofessionalnasir_ml19-Aug-14 23:51 
QuestionGreat Work! Pinprofessionaleslipak19-Aug-14 10:16 
QuestionGreat Work Pinprofessionaleslipak19-Aug-14 10:10 
AnswerRe: Great Work Pinprofessionalnasir_ml19-Aug-14 19:31 
GeneralRe: Great Work Pinprofessionaleslipak20-Aug-14 12:13 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141216.1 | Last Updated 19 Aug 2014
Article Copyright 2014 by nasir_ml
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid