HTMLタグにdatasetを指定して進捗サークルチャートを表示させるスクリプト

[こちらの記事](https://qiita.com/akebi_mh/items/744efe3bb4e53a5a19f7)の続きです。

HTMLドキュメント内のタグにdatasetを指定して進捗サークルチャートを表示させるためのスクリプトです。
ユーザー操作や時間経過等で可変させることは想定していません。

###スクリプト###

```js:circle_chart.js
'use strict';
{
    window.addEventListener('DOMContentLoaded', function() {
        for(const e of document.querySelectorAll('[data-circleChart]')) {
            if(e.querySelector('.circleChart') === null) {
                e.innerHTML +=
                    '<div class="circleChart">' +
                    '  <div class="graph"></div>' +
                    '  <div class="percent"><div>**.*%</div></div>' +
                    '</div>';

                const
                    d1 = e.querySelector('.circleChart'),
                    d2 = d1.querySelector('.graph'),
                    d3 = d1.querySelector('.percent');

                d1.style.position = 'relative';
                d1.style.display = 'inline-block';

                d2.style.position = 'absolute';
                d2.style.mixBlendMode = 'multiply';

                d3.style.position = 'absolute';
                d3.style.width = '100%';
                d3.style.height = '100%';

                const params = e.dataset.circlechart;

                let size        = 100,
                    weight      = 10,
                    color       = '#08f',
                    backcolor   = '#ccc',
                    holecolor   = 'transparent',
                    percent     = 50,
                    fixed       = 1,
                    textcolor   = '#fff',
                    textmixblendmode = 'difference',
                    textleft    = '',
                    textright   = '',
                    texttop     = '',
                    textbottom  = '',
                    textdisplay = '';

                params.split(';').forEach(v => {
                    if(v.trim() !== '') {
                        const
                            param = v.split(':'),
                            key = param[0].trim().toLowerCase(),
                            value = param[1].trim().toLowerCase();

                        if(key === 'size') {
                            size = parseInt(value);
                        }
                        else if(key === 'weight') {
                            weight = parseFloat(value);
                            if(weight <= 0) weight = -100;
                        }
                        else if(key === 'color') {
                            if(checkProperty(value)) color = value;
                        }
                        else if(key === 'backcolor') {
                            if(checkProperty(value)) backcolor = value;
                        }
                        else if(key === 'holecolor') {
                            if(checkProperty(value)) holecolor = value;
                        }
                        else if(key === 'textcolor') {
                            if(checkProperty(value)) textcolor = value;
                        }
                        else if(key === 'textmixblendmode') {
                            if(checkProperty(value, 'mixBlendMode')) textmixblendmode = value;
                        }
                        else if(key === 'textleft') {
                            if(checkProperty(value, 'left')) textleft = value;
                        }
                        else if(key === 'textright') {
                            if(checkProperty(value, 'right')) textright = value;
                        }
                        else if(key === 'texttop') {
                            if(checkProperty(value, 'top')) texttop = value;
                        }
                        else if(key === 'textbottom') {
                            if(checkProperty(value, 'bottom')) textbottom = value;
                        }
                        else if(key === 'textdisplay') {
                            if(checkProperty(value, 'display')) textdisplay = value;
                        }
                        else if(key === 'value') {
                            percent = parseFloat(value);
                        }
                        else if(key === 'fixed') {
                            fixed = Math.floor(value);
                            if(fixed < 0) fixed = 0;
                            else if(fixed > 10) fixed = 10;
                        }
                    }
                });

                d1.style.width  =
                d1.style.height =
                d2.style.width  =
                d2.style.height = `${size}px`;

                const r1 = size / 2;
                const r2 = r1 - r1 / 100 * weight;
                const deg = percent * 3.6;

                d1.style.background = `radial-gradient(${holecolor} ${r2 - 1}px, #fff ${r2}px, #fff ${r1 - 1}px, #0000 ${r1}px)`;
                d2.style.background = `radial-gradient(#fff ${r2 - 1}px, #0000 ${r2}px, #0000 ${r1 - 1}px, #fff ${r1}px), conic-gradient(${color} ${deg}deg, ${backcolor} 0)`;

                const d3c = d3.querySelector('div');
                d3c.textContent = `${percent.toFixed(fixed)}%`;

                d3.style.fontSize = `${(r1 / 4).toFixed(1)}px`;
                d3.style.color = textcolor;
                d3.style.mixBlendMode = textmixblendmode;

                if(textleft === '' && textright === '') {
                    d3c.style.position = 'relative';
                    d3.style.textAlign = 'center';
                }
                else {
                    d3c.style.position = 'absolute';
                    if(textleft !== '') d3c.style.left = textleft;
                    if(textright !== '') d3c.style.right = textright;
                }
                if(texttop === '' && textbottom === '') {
                    d3c.style.top = '60%';
                }
                else {
                    if(texttop !== '') d3c.style.top = texttop;
                    if(textbottom !== '') d3c.style.bottom = textbottom;
                }
                if(textdisplay !== '') d3c.style.display = textdisplay;

                function checkProperty(str, p) {
                    const dummy = document.createElement('p');
                    if(p === undefined) p = 'color';
                    dummy.style[p] = str;
                    return dummy.style[p] === '' ? false : true;
                }
            }
        }
    });
}
```

###使用方法###

```<script src='circle_chart.js'></script>```でスクリプトを読み込んでおき、チャートを表示させたい要素に**data-circleChart**を付加することで表示できます。

```html
<div data-circleChart='プロパティリスト'></div>
```

以下のプロパティを指定できます。

**size**: チャートの直径(pixel)
**weight**: チャートの半径に対する太さの割合(0～100)
**color**: チャートのメインカラー(CSSのcolorで指定可能な値)
**backColor**: チャートのバックカラー(CSSのcolorで指定可能な値)
**holeColor**: チャートの穴の色(CSSのcolorで指定可能な値 デフォルトは透明)
**textColor**: 文字カラー(CSSのcolorで指定可能な値)
**textMixBlendMode**: 文字ブレンドモード(CSSのmix-blend-modeで設定可能な値 デフォルトはdifference)
**textLeft**: 文字の横表示位置 左基準(CSSのleftで設定可能な値)
**textRight**: 文字の横表示位置 右基準(CSSのrightで設定可能な値)
　　textLeft,textRight共に無指定の場合は中央に表示
**textTop**: 文字の縦表示位置 上基準(CSSのtopで設定可能な値)
**textBottom**: 文字の縦表示位置 下基準(CSSのbottomで設定可能な値)
　　textTop,textBottom共に無指定の場合は上から60%の位置に表示
**textDisplay**: 文字の表示形式(CSSのdisplayで設定可能な値 非表示にするためにnoneを指定するケースが多いと思います)
**value**: チャートの進捗度(0～100)
**fixed**: 進捗度の数字の少数表示桁数(0～10)

###サンプル###

[**動作デモ**](https://akebi.jp/temp/circle_chart/)

```html
<script src='./circle_chart.js'></script>

<body bgcolor='#ddeedd'>
    <!-- プロパティ無指定 -->
    <div data-circleChart></div>

    <!-- プロパティ指定例 -->
    <span data-circleChart='size:150; weight:100; backColor:#fca; color:#fa0; value:45.67; fixed:1'></span>
    <span data-circleChart='size:200; value:56.7; fixed:0; weight:30; color:#f8a; holecolor:pink; textColor:red; textMixBlendMode:normal; textright:5%; textTop:44%'></span>
    <div data-circleChart='textDisplay:none;'></div>

    <!-- 親要素のstyle併用 -->
    <div data-circleChart='size:150; fixed:2' style='position:absolute; top:5px; right:5px;font-family:serif;'></div>
</body>
```