Drop-down single-select multi-select, Lenovo radio-select input, Lenovo multi-select input menu components (cs-select) based on Web components

1. Template of HTML elements

Code:

const templateElem = document.createElement("template");
templateElem.innerHTML = `
    <div id="root">
        <style>
            .cs-select{
                border: 0px;
                width: 100%;
                height: 100%;
                position: relative;
                display:flex;
                align-items: center;
                justify-content: center;
            }
            .cs-select:empty::before {
                content: attr(placeholder);
            }
            .cs-select-box{
                padding: 2px 2px 2px 2px;
                border: 1px solid #000;
                background: #fff; 
                height: 120px;
                width: 120px;
                overflow-y: scroll; 
                position: absolute;
                z-index: 9999;
                display: none
            }
            .cs-select-box li {
                white-space: wrap;
            }
            .cs-select-box li:hover {
                background-color: #e0e0e0;
            }
            .cs-select-box::-webkit-scrollbar{
                height: 8px;
                width: 8px;
            }

            .cs-select-box::-webkit-scrollbar-thumb {
                border-radius: 10px;
                border-style: dashed;
                border-color: transparent;
                border-width: 6px;
                background-color: rgba(157, 165, 183, 0.4);
                background-clip: padding-box;
            }

            .cs-select-box::-webkit-scrollbar-thumb:hover {
                background: rgba(157, 165, 183, 0.7);
            }
            .cs-select-box input[type="radio"]{
                display: none
            }
            .cs-select-box div:hover{
                background-color: #e0e0e0;
            }
        </style>
        <div class="cs-select"></div>
        <div class="cs-select-box">
        </div>
    </div>
`

2. The core code of the class class

1. Constructor

    constructor() {
    
    
        super();
        var oldOptions = [];
        const shadow = this.attachShadow({
    
     mode: 'open' });
        const content = templateElem.content.cloneNode(true);

        this.name = this.getAttribute("name");
        this.cho = this.getAttribute("cho");
        this.isAsso = this.getAttribute("isAsso");
        this.url = this.getAttribute("url");
        this.mapping = JSON.parse(this.getAttribute("mapping"));

        if(this.getAttribute("url") === "" && this.getAttribute("getDataByLS") === "false") {
    
    this.getOptions(oldOptions);}
        else if(this.getAttribute("url") !== "" && this.getAttribute("getDataByLS") === "false"){
    
    
            this.type = this.getAttribute("type");
            this.getOptionsByURL(oldOptions, this.url, this.type, this.mapping);
        }
        else if(this.getAttribute("getDataByLS") === "true"){
    
    
            this.nameforLS = this.getAttribute("nameforLS");
            this.getOptionByLocalStorage(oldOptions, window.localStorage, this.nameforLS, this.mapping);
            top.window.addEventListener('storage', event => {
    
    
                if(event.key === this.nameforLS){
    
    
                    this.getOptionByLocalStorage(oldOptions, window.localStorage, this.nameforLS, this.mapping);
                }
            });
        }
        else if(this.getAttribute("getDataBySQLlite" === "true")){
    
    
            this.SQLliteKey = this.getAttribute("SQLliteKey");
            this.getOptionBySQLlite(oldOptions, this.SQLliteKey, this.mapping);
        }

        if(this.cho === "checkbox" && this.isAsso === "false"){
    
    //为普通多选框
            //调用插入数据函数
            this.setBoxforCheckbox(content, oldOptions);
            shadow.appendChild(content);

            this.$sele = shadow.querySelector(".cs-select");//获得显示框
            //为显示框添加cho属性
            this.$sele.setAttribute("cho", this.cho);
            this.$sele.setAttribute("name", this.name);
            this.$box = shadow.querySelector(".cs-select-box");//获得下拉菜单
            //加入事件监听
            this.$sele.addEventListener('click', function(e){
    
    
                e.preventDefault();
                let pthis = $(this);
                if(pthis.attr('show-select') == 'true'){
    
    
                    pthis.attr('show-select', 'false');
                    pthis.next().hide();
                    return;
                } else {
    
    
                    pthis.attr('show-select', 'false');
                    pthis.attr('show-select', 'true');
                    pthis.next().show();
                }
                pthis.next().find('input').on('change',function(){
    
    
                    var $this = $(this);
                    var $sele = $this.parent().parent();//找到cs-select-box
                    $sele.prev().empty();
                    let divs = $sele.find('div');
                    for(let i = 0; i < divs.length; i++) {
    
    
                        if(divs.eq(i).find('input').prop('checked') && $sele.perv().html().length == 0){
    
    
                            $sele.prev().attr("style", "border: 0px;width: 100%;height: 100%;text-align: center;");
                            $sele.prev().append('' + divs.eq(i).find('span').html() + '');
                        }
                        else if(divs.eq(i).find('input').prop('checked')){
    
    
                            $sele.prev().attr("style", "border: 0px;width: 100%;height: 100%;text-align: left;");
                            $sele.prev().append(";" + divs.eq(i).find('span').html() + '');
                        } 
                    }
                });
                //失焦逻辑(以name属性为唯一性区分,作为失焦事件的判断)
                top.window.$(document).click(pthis,function(e){
    
    
                    if(e.target.getAttribute("name") !== pthis[0].getAttribute("name")){
    
    
                        pthis[0].setAttribute('show-select', 'false');
                        pthis.next().hide();
                    }
                });
            });
        }
        else if(this.cho === "radio" && this.isAsso === "false"){
    
    //为普通单选框
            this.setBoxforRadio(content, oldOptions);
            shadow.appendChild(content);

            this.$sele = shadow.querySelector(".cs-select");//获得显示框
            //为显示框添加cho属性
            this.$sele.setAttribute("cho", this.cho);
            this.$sele.setAttribute("name", this.name);
            this.$box = shadow.querySelector(".cs-select-box");//获得下拉菜单

            //加入事件监听
            this.$sele.addEventListener('click', function(e){
    
    
                e.preventDefault();
                let pthis = $(this);
                if(pthis.attr('show-select') == 'true'){
    
    
                    pthis.attr('show-select', 'false');
                    pthis.next().hide();
                    return;
                } else {
    
    
                    pthis.attr('show-select', 'false');
                    pthis.attr('show-select', 'true');
                    pthis.next().show();
                }
                //失焦逻辑(以name属性为唯一性区分,作为失焦事件的判断)
                top.window.$(document).click(pthis,function(e){
    
    
                    if(e.target.getAttribute("name") !== pthis[0].getAttribute("name")){
    
    
                        pthis[0].setAttribute('show-select', 'false');
                        pthis.next().hide();
                    }
                });
            });
        }
        else if(this.cho == "checkbox" && this.isAsso === "true"){
    
    //为联想多选框
            //由于后续会对下拉数据做处理,且不能删除原来的oldOptions列表,故维持一个新的数组用于联想的下拉
            var newOptions = oldOptions;//这个数组最开始应该等于oldOptions

            this.$sele = content.querySelector(".cs-select");//获得显示框
            //为显示框添加cho属性
            this.$sele.setAttribute("cho", this.cho);
            this.$sele.setAttribute("name", this.name);
            var input = document.createElement("textarea");
            input.setAttribute("type", "text");
            input.setAttribute("style", "border: 0;height: 100%;width: 100%;resize:none;text-align:center;vertical-align:middle");
            this.$sele.appendChild(input);
            //为cs-select内的input检查有无添加placeholder文本(有则预显示,无则不显示预加载文本)
            if(this.getAttribute("placeholder") !== ""){
    
    
                $(this.$sele).find("textarea").attr("placeholder", this.getAttribute("placeholder"));
            }
            this.$box = content.querySelector(".cs-select-box");//获得下拉菜单
            //为下拉框初始化内容
            this.setBoxforAsso("checkbox", content, oldOptions, newOptions, $(this.$sele).find("textarea").val(), this.$sele, this.$box);
            shadow.appendChild(content);
            //调整下拉菜单最大高度
            $(this.$box).css({
    
    
                'max-height': '300px',
                'background-color': 'white',
                'overflow-y': 'scroll',
                'z-index': '91'
            });
            //输入文本时调用展示下拉菜单函数
            $(this.$sele).find("textarea").on('input propertychange', ()=>{
    
    
                var pthis = $(this.$sele).find("textarea");
                var tag = pthis.val();
                var tagstr = tag.split(/[,,,,;,;, ]/)[tag.split(/[,,,,;,;, ]/).length - 1];
                if (tagstr != "") {
    
    
                    $(this.$sele).next().find("ul").empty();
                    this.setBoxforAsso("checkbox", content, oldOptions, newOptions, tagstr, this.$sele, this.$box);
                }
                else{
    
    
                    $(this.$sele).next().find("ul").empty();
                    this.setBoxforAsso("checkbox", content, oldOptions, newOptions, tagstr, this.$sele, this.$box);
                }
            });

            //为cs-select添加事件监听(点击就显示下拉菜单、输入改变时修正菜单内容)
            this.$sele.addEventListener('click', function(e){
    
    
                e.preventDefault();
                let pthis = $(this);
                if(pthis.attr('show-select') == 'true'){
    
    
                    pthis.attr('show-select', 'false');
                    pthis.next().hide();
                    return;
                } else {
    
    
                    pthis.attr('show-select', 'false');
                    pthis.attr('show-select', 'true');
                    pthis.next().show();
                }
                //失焦逻辑(以name属性为唯一性区分,作为失焦事件的判断)
                top.window.$(document).click(pthis,function(e){
    
    
                    if(e.target.getAttribute("name") !== pthis[0].getAttribute("name")){
    
    
                        pthis[0].setAttribute('show-select', 'false');
                        pthis.next().hide();
                    }
                });
            });

        }
        else if(this.cho == "radio" && this.isAsso === "true"){
    
    //为联想单选框
            //由于后续会对下拉数据做处理,且不能删除原来的oldOptions列表,故维持一个新的数组用于联想的下拉
            var newOptions = oldOptions;//这个数组最开始应该等于oldOptions

            this.$sele = content.querySelector(".cs-select");//获得显示框
            //为显示框添加cho属性
            this.$sele.setAttribute("cho", this.cho);
            this.$sele.setAttribute("name", this.name);
            var input = document.createElement("input");
            input.setAttribute("type", "text");
            input.setAttribute("style", "border: 0;height: 100%;width: 100%;");
            this.$sele.appendChild(input);
            //为cs-select内的input检查有无添加placeholder文本(有则预显示,无则不显示预加载文本)
            if(this.getAttribute("placeholder") !== ""){
    
    
                $(this.$sele).find("input").attr("placeholder", this.getAttribute("placeholder"));
            }
            this.$box = content.querySelector(".cs-select-box");//获得下拉菜单
            //为下拉框初始化内容
            this.setBoxforAsso("radio", content, oldOptions, newOptions, $(this.$sele).find("input").val(), this.$sele, this.$box);
            shadow.appendChild(content);
            //调整下拉菜单最大高度
            $(this.$box).css({
    
    
                'max-height': '300px',
                'background-color': 'white',
                'overflow-y': 'scroll',
                'z-index': '91'
            });
            //输入文本时调用展示下拉菜单函数
            $(this.$sele).find("input").on('input propertychange', ()=>{
    
    
                var pthis = $(this.$sele).find("input");
                if (pthis.val() != "") {
    
    
                    this.setBoxforAsso("radio", content, oldOptions, newOptions, pthis.val(), this.$sele, this.$box);
                }
                else{
    
    
                    this.setBoxforAsso("radio", content, oldOptions, newOptions, pthis.val(), this.$sele, this.$box);
                }
            });

            //为cs-select添加事件监听(点击就显示下拉菜单、输入改变时修正菜单内容)
            this.$sele.addEventListener('click', function(e){
    
    
                e.preventDefault();
                let pthis = $(this);
                if(pthis.attr('show-select') == 'true'){
    
    
                    pthis.attr('show-select', 'false');
                    pthis.next().hide();
                    return;
                } else {
    
    
                    pthis.attr('show-select', 'false');
                    pthis.attr('show-select', 'true');
                    pthis.next().show();
                }
                //失焦逻辑(以name属性为唯一性区分,作为失焦事件的判断)
                top.window.$(document).click(pthis,function(e){
    
    
                    if(e.target.getAttribute("name") !== pthis[0].getAttribute("name")){
    
    
                        pthis[0].setAttribute('show-select', 'false');
                        pthis.next().hide();
                    }
                });
            });
            //失焦逻辑(以name属性为唯一性区分,作为失焦事件的判断)
            top.window.$(document).click(pthis,function(e){
    
    
                if(e.target.getAttribute("name") !== pthis[0].getAttribute("name")){
    
    
                    pthis[0].setAttribute('show-select', 'false');
                    pthis.next().hide();
                }
            });
        }
    }

2. How to obtain data

(1) From the <option> tag:

Code:

    getOptions(oldOptions) {
    
    //从option标签获取选项,重新加入到oldOptions里
        var options = this.querySelectorAll("option");
        for(var i = 0; i < options.length; i++){
    
    
            var tmp = new Object();
            let value = options[i].value;
            let name = options[i].innerHTML;
            tmp.value = value;
            tmp.name = name;
            oldOptions.push(tmp);
        }
    }

(2) Request from the network:

Code:

    getOptionsByURL(oldOptions, url, type, mapping) {
    
    //从对应的top.getDataByTag函数获取选项,重新加入到oldOptions及box里
        var urlpath = url;//后端给的接口
        var data;
        $.ajax({
    
    
            url:  top.rootIp + '/' + urlpath,
            type: type,
            data: data,
            dataType: 'json',
            async: false,
            beforeSend: function (xhr) {
    
    
                xhr.setRequestHeader("x-access-tokens", `${
      
      top.loginToken}`);
            },
            success: function (res) {
    
    
                if(res.success == false){
    
    }
                else if(res.success != undefined){
    
    
                    data = res['data'];
                    var nametag = mapping['name'];
                    var idtag = mapping['id'];
                    for (let i = 0; i < data.length; i++) {
    
    
                        var tmp = new Object();
                        let value = data[i][idtag];
                        let name = data[i][nametag];
                        tmp.value = value;
                        tmp.name = name;
                        oldOptions.push(tmp);
                    }
                }
            },
            error: function (e) {
    
    
                if(obj.error != undefined){
    
    
                    obj.error(e);
                }
                top.log('network_error',e);
            }
        });
    }

(3) Get from localStorage:

Code:

    getOptionByLocalStorage(oldOptions, localStorage, nameforLS, mapping){
    
    
        var data;
        var nametag = mapping['name'];
        var idtag = mapping['id'];
        var stringdata = localStorage.getItem(nameforLS);
        data = JSON.parse(stringdata);

        for(var i = 0; i < data.length; i++){
    
    
            var tmp = new Object();
            let value = data[i][idtag];
            let name = data[i][nametag];
            tmp.value = value;
            tmp.name = name;
            oldOptions.push(tmp);
        }
    }

(4) Get from SQLlite:

Code:

    getOptionBySQLlite(oldOptions, SQLliteKey, mapping) {
    
    
        //should attention 依赖于top.selectOfflineServerData的实现(从本地数据库SQLlite获取数据)
        top.selectOfflineServerData(url, function(data){
    
    
            var nametag = mapping['name'];
            var idtag = mapping['id'];
            for (let i = 0; i < data.length; i++) {
    
    
                var tmp = new Object();
                let value = data[i][idtag];
                let name = data[i][nametag];
                tmp.value = value;
                tmp.name = name;
                oldOptions.push(tmp);
            }
        })
    }

3. Set the options in the internal drop-down box after getting the data from the outside world

//多选下拉菜单的设置:
    setBoxforCheckbox(content, oldOptions){
    
    //将获取好的内容设置到下拉菜单div中
        content.querySelector(".cs-select-box").innerHTML = "";//首先清空,避免重复加入
        for(var i=0; i<oldOptions.length; i++){
    
    //循环加入
            var oneDiv = document.createElement("div");
            oneDiv.setAttribute("style", "float: left;");
            oneDiv.innerHTML = "<input type='checkbox' value='" + oldOptions[i]['value'] + "' name='checkbox' id='checkbox_" +
                oldOptions[i]['value'] + "'/><span>" + oldOptions[i]['name'] + "</span>";
            content.querySelector(".cs-select-box").appendChild(oneDiv);
        }
    }
//单选下拉菜单的设置:
    setBoxforRadio(content, oldOptions){
    
    //同上
        content.querySelector(".cs-select-box").innerHTML = "";//首先清空,避免重复加入
        var oneDiv = document.createElement("div");
        oneDiv.setAttribute("style", "float: left;width: 100%;");
        oneDiv.innerHTML = "<input type='radio' value='' name='radio' id='radio_-1'/><span>请选择</span>";
        content.querySelector(".cs-select-box").appendChild(oneDiv);
        for(var i = 0; i<oldOptions.length; i++){
    
    
            var oneDiv = document.createElement("div");
            oneDiv.setAttribute("style", "float: left;width: 100%;");
            oneDiv.innerHTML = "<input type='radio' value='" + oldOptions[i]['value'] + "' name='radio' id='radio_" +
                oldOptions[i]['value'] + "'/><span οnclick=''>" + oldOptions[i]['name'] + "</span>";
            content.querySelector(".cs-select-box").appendChild(oneDiv);
        }
        //为每个div绑定事件
        var divList = $(content.querySelector("div.cs-select-box")).find("div");
        for(var i = 0; i < divList.length; i++){
    
    
            divList.eq(i).click(function(){
    
    
                $(this).find("input").attr("checked",true);
                $(this).parent().prev().attr("style", "border: 0px;width: 100%;height: 100%;text-align: center;");
                $(this).parent().prev().html($(this).find("span").html());
                $(this).parent().prev().attr('show-select', 'false');
                $(this).parent().hide();
            });
        }
    }
/联想输入下拉的设置判断(区分单、多选)
    setBoxforAsso(str, oldOptions, newOptions, tag, box){
    
    
        if(str === "radio"){
    
    
            this.showBoxAndsetRadio(oldOptions, newOptions, tag, box);
        }
        else if(str === "checkbox"){
    
    
            this.showBoxAndsetCheckbox(oldOptions, newOptions, tag, box);
        }
    }

4. Read the selected value and set the selected value externally from the custom tag (get value(), set value(value))

    //get value 和set value
    set value(str){
    
    
        var strList = str.split(/[ ,,,;,;, ]/);
        if(this.shadowRoot.querySelector("#root > div.cs-select > input") === null && this.shadowRoot.querySelector("#root > div.cs-select > textarea") === null){
    
    
            var optionList = this.shadowRoot.querySelector("#root > div.cs-select-box").querySelectorAll("div");
            var j = 0;
            this.shadowRoot.querySelector("#root > div.cs-select").innerHTML = "";
            for(var i = 0; i < optionList.length; i++){
    
    
                if(strList[j] === optionList[i].querySelector("span").innerHTML){
    
    
                    optionList[i].querySelector("input").checked = true;//选中选项
                    //为上方显示框加入字符串
                    if(j != strList.length -1 && strList.length > 1){
    
    
                        this.shadowRoot.querySelector("#root > div.cs-select").innerHTML += (strList[j] + ";");
                    }
                    else if(strList.length === 1){
    
    
                        this.shadowRoot.querySelector("#root > div.cs-select").setAttribute("style", "border: 0px;width: 100%;height: 100%;text-align: center;");
                        this.shadowRoot.querySelector("#root > div.cs-select").innerHTML += strList[j];
                    }
                    else{
    
    
                        this.shadowRoot.querySelector("#root > div.cs-select").innerHTML += strList[j];
                    }
                    j++;
                }
            }
        }
        else if(this.shadowRoot.querySelector("#root > div.cs-select > input") !== null){
    
    
            $(this.shadowRoot.querySelector("#root > div.cs-select > input")).val("");
            for(var i = 0; i < strList.length; i++){
    
    
                $(this.shadowRoot.querySelector("#root > div.cs-select > input")).val($(this.shadowRoot.querySelector("#root > div.cs-select > input")).val() + ";" + strList[i]);
            }
        }
        else if(this.shadowRoot.querySelector("#root > div.cs-select > textarea") !== null){
    
    
            $(this.shadowRoot.querySelector("#root > div.cs-select > textarea")).val("");
            for(var i = 0; i < strList.length; i++){
    
    
                $(this.shadowRoot.querySelector("#root > div.cs-select > textarea")).val($(this.shadowRoot.querySelector("#root > div.cs-select > textarea")).val() + ";" + strList[i]);
            }
        }
    }

    get value(){
    
    
        var str="";
        if(this.shadowRoot.querySelector("#root > div.cs-select > input") === null && this.shadowRoot.querySelector("#root > div.cs-select > textarea") === null){
    
    
            var optionList = this.shadowRoot.querySelector("#root > div.cs-select-box").querySelectorAll("div");
            for(var i=0; i<optionList.length; i++){
    
    
                if(optionList[i].querySelector("input").checked) str += (optionList[i].querySelector("span").innerHTML + " ");
            }
            return str;
        }
        else if(this.shadowRoot.querySelector("#root > div.cs-select > input") !== null){
    
    
            return $(this.shadowRoot.querySelector("#root > div.cs-select > input")).val();
        }
        else if(this.shadowRoot.querySelector("#root > div.cs-select > textarea") !== null){
    
    
            return $(this.shadowRoot.querySelector("#root > div.cs-select > textarea")).val();
        }
    }

3. Associate the above Select class with the custom label:

window.customElements.define('cs-select', Select);

4. How to use

Introduce JS

<script src="../ce-select.js"></script>

1. Syntax explanation (with [] as optional field)

<cs-select [style="height: 100%;width: 100%;"] 
        name="theName" placeholder="请输入"
        cho="radio/checkbox" isAsso="false/true" 
        url="/oneSend" [type="get/post"] 
        getDataByLS="false/true" [nameforLS="oneNameforLS"] 
        getDataBySQLlite="false/true" [SQLliteKey="oneKeyforSQLlite"]
        mapping='{}'>
        <option></option>
        ......
</cs-select>

(1)name:

The identification attribute used to distinguish it from other labels with the same label must have a unique name corresponding to a drop-down;

(2)placeholder:

The default text when the display box is empty.

(3)for:

Used to distinguish between multi-selection and single-selection, where radio corresponds to single-selection; checkbox corresponds to multi-selection;

(4)isAce:

A sign to distinguish whether it is an associative input. It is used when developing the drop-down input of an associative input. False means that it is not an associative input, and true means that it is an associative input;

(5)url:

The network request link is used internally by handing it to $.ajax, and its return value should be a dictionary corresponding to the option value and name:

{
    
    
	data:[
			{
    
    "valueOfId": "number_XXX","valueOfName": "name_XXX"},
			{
    
    "valueOfId": "number_XXX","valueOfName": "name_XXX"},
			{
    
    "valueOfId": "number_XXX","valueOfName": "name_XXX"},
			......
		]
	success: true
}
或者:
{
    
    
	data: [],
	success: false
}

(6)type:

This is the way to obtain the previous URL, there are two ways: "get" and "post" (if the previous URL attribute value is empty, this attribute does not need to be specified)

(7)getDataByLS:

Flag indicating whether to obtain data from localStorage. There are two types: false and true.

(8)nameforLS:

Represents which keyword of localStorage to obtain data from (if the getDataByLS attribute value is false, it does not need to be specified).

(7)getDataBySQLlite:

Flag indicating whether to obtain data from SQLlite. There are two types: false and true.

(8)SQLliteKey:

Represents which keyword of SQLlite to obtain data from (if the getDataBySQLlite attribute value is false, it does not need to be specified).

(9)mapping:

The format is a json, which represents the mapping relationship. It is used to avoid the problem of inconsistency between the id and name field keys in the data obtained by different network requests and the ones used internally in the class. The internal defaults of the class are id and name. If you want to change the internal data acquisition of the class, To obtain the field key, indicate it in the mapping attribute.
! ! ! Note: If you want to use the default key mapping relationship, the mapping attribute must be set to {}:

	mapping="{}"

(10) <option></option> tag: used as an option to load into the drop-down box, it must have a value attribute (can be empty).

The value attribute value will be used as the id corresponding to the internal drop-down option, and the text inside <option></option> will be used as the text in <span></span> of the drop-down option.

2. Use labels to add options to custom drop-down boxes

example:

    <option value="0">str0</option>
    <option value="1">str1</option>
    <option value="2">str2</option>

3. Use URL to add drop-down options to <cs-select></cs-select>:

<cs-select 
	name="theName" placeholder="请输入"
    cho="radio" isAsso="false" 
    url="/getdata" type="get" 
    getDataByLS="false" 
    getDataBySQLlite="false"
    mapping='{
     
     "id":"xuhao","name":"xuanxiangmingcheng"}'>
</cs-select>

4.5. The usage syntax of localStorage and SQLlite is similar to 2.

5. Examples (omitted)

Guess you like

Origin blog.csdn.net/weixin_47278656/article/details/130020831