美化select下拉框

在寫示例的時候,用到了下拉框,但是原生的下拉框是在是有點難看,然後模仿着寫了點,一個是直接在寫好的Dom上進行美化,一個是用js生成,然後定義類名及相應的事件來處理

1.效果圖

clipboard.png

2.直接是在Dom上美化

  • html文件
<div class="root">
    <div id="selectedItem">
        <div id="promptText"><span id="spanText">請選擇你喜歡的文字</span><img src="../images/arrowup.png" id="arrows" /></div>
        <ul class="choiceDescription">
            <li class="item">萬水千山,陪你一起看</li>
            <li class="item">萬水千山,陪你一起看1</li>
            <li class="item">萬水千山,陪你一起看2</li>
            <li class="item">萬水千山,陪你一起看3</li>
            <li class="item">萬水千山,陪你一起看4</li>
        </ul>
    </div>
</div>
  • css文件
ul{
    margin: 0;
    padding: 0;
    list-style: none;
}
/* 下拉框包含層 */
#selectedItem{
    width: 240px;
    cursor: pointer;
}
/* 已選中的選項 */
#promptText{
    position: relative;
    padding-left: 10px;
    width: 230px;
    height: 30px;
    line-height: 30px;
    border: 1px solid #d3d3d3;
    border-radius: 4px;
    background: #fff;
    color: #999;
    font-size: 14px;
}
/* 圖標 */
#arrows{
    position: absolute;
    top: 0;
    right: 0;
    width: 30px;
    height: 30px;
    vertical-align: middle;
}
#arrows:focus{
    outline: none;
}
/* 下拉可選項包含層 */
.choiceDescription{
    position: absolute;
    display: none;
    /*overflow: hidden;*/
    margin-top: 2px;
    width: 240px;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .1);
    background: #fff;
}
.show{
    display: block;
}
/* 下拉可選項 */
.item{
    height: 30px;
    line-height: 30px;
    padding-left: 10px;
    font-size: 15px;
    color: #666;
}
.item:hover{
    color: #fff;
    background: rgba(49, 255, 195, 0.67);
}
  • js文件
(function() {
    let choiceDescription = document.getElementsByClassName('choiceDescription')[0];
    let arrows = document.getElementById('arrows');
    /* 用於判斷是否是下拉 */
    let isDown = false;

    let selectedItem = document.getElementById('selectedItem');
    /* 對點擊下拉進行監聽 */
    selectedItem.addEventListener('click', function() {
        isDown = !isDown;
        if(isDown) {
            /* 如果是下拉狀態,則顯示下拉的選項,並把圖標顯示爲向下的圖標 */
            choiceDescription.className += ' show';
            arrows.src = '../images/arrowdown.png';
        } else {
            choiceDescription.className = 'choiceDescription';
            arrows.src = '../images/arrowup.png';
        }
    });

    choiceDescription.addEventListener('click', function(e) {
        let promptText = document.getElementById('spanText');
        let selectElement = e.target;

        /* 判斷是否點擊的是li標籤,防止點擊了li標籤以外的空白位置 */
        while(selectElement.tagName !== 'LI') {

            /* 如果點中的是當前容器層 */
            if(selectElement == choiceDescription) {
                selectElement = null;
                break;
            }

            /* 若果不是,則再找父級容器 */
            selectElement = selectElement.parentNode;
        }

        /* innerText、innerHTML、value
        * innerText 是指html標籤裏的文字信息,單純的文本,不會有html標籤,存在兼容性
        * innerHTML 是指包含在html標籤裏的所有子元素,包括空格、html標籤
        * value 表單裏的元素屬性值
        * */
        if(selectElement) {
            promptText.innerHTML = e.target.innerHTML;
        }
    });
})()
  • 在已有的Dom節點上對Dom綁定事件,我這裏的寬度是固定死的,相對來說不是很友好

3.js自動生成進行美化

  • html文件
<div id="select" class="select"></div>

<script src="autoGenerateSelect.js"></script>
<script>
    (function() {

        /* 當 onload 事件觸發時,頁面上所有的DOM,樣式表,腳本,圖片,flash都已經加載完成了
         * 當 DOMContentLoaded 事件觸發時,僅當DOM加載完成,不包括樣式表,圖片,flash
         */
        document.addEventListener('DOMContentLoaded',function(){
            new $Selector({
                elementSelector:'#select',
                options:[
                    {name:'選項1',value:'0'},
                    {name:'選項2',value:'1'},
                    {name:'選項3',value:'2'}
                ],
                defaultText:'選項2'
            });
        })
    })()
</script>

*css文件

html, body, ul{
    margin: 0;
    padding: 0;
}
ul{
    list-style: none;
}
#select{
    padding: 30px 40px 0;
}
/* 下拉框 */
.dropDown{
    position: relative;
    display: inline-block;
    min-width: 120px;
    box-sizing: border-box;
    color: #515a6e;
    font-size: 14px;
}
/* 已選中的值包含層 */
.selectedOption{
    position: relative;
    box-sizing: border-box;
    outline: none;
    user-select: none;
    cursor: pointer;
    background: #fff;
    border-radius: 4px;
    border: 1px solid #dcdee2;
    transition: all .2s ease-in-out;
}
.selectedValue{
    display: block;
    overflow: hidden;
    height: 28px;
    line-height: 28px;
    font-size: 12px;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-left: 8px;
    padding-right: 24px;
}
/* 圖標 */
.arrowDown{
    position: absolute;
    display: inline-block;
    top: 50%;
    right: 8px;
    margin-top: -7px;
    font-size: 14px;
    color: #808695;
    transition: all .2s ease-in-out;
    /* 字體抗鋸齒渲染 */
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}
.arrowDown:before{
    content: "";
    display: block;
    width: 6px;
    height: 6px;
    background-color: transparent;
    border-left: 2px solid #808695;
    border-bottom: 2px solid #808695;
    transform: rotate(-45deg);
}

/* 所有選項的包含層 */
.optionsContainer{
    position: absolute;
    top: 30px;
    left: 0;
    min-width: 120px;
    max-height: 200px;
    margin: 5px 0;
    padding: 5px 0;
    background: #fff;
    box-sizing: border-box;
    border-radius: 4px;
    box-shadow: 0 1px 6px rgba(0, 0, 0, .2);
    z-index: 2;
    transform-origin: center top 0px;
    transition: all 0.3s;
    will-change: top, left;

    transform: scale(1, 0);
    opacity: 0;
}

/* 每個選項 */
.optionsItem{
    line-height: normal;
    padding: 7px 16px;
    color: #515a6e;
    font-size: 12px;
    white-space: nowrap;
    cursor: pointer;
    transition: background .2s ease-in-out;
}
.itemSelected, .optionsItem:hover{
    color: #2d8cf0;
    background-color: #f3f3f3;
}
  • 對下拉框初始化
/* 私有方法:初始化下拉框 */
_initSelector({
    /* 傳入id,class,tag,用於掛載下拉框 */
    elementSelector = '',

    /* 傳入的下拉框選項 */
    options = [{
        name: '請選擇你喜歡的顏色',
        value: '0'
    }],
    defaultText = '請選擇你喜歡的顏色'
}) {
    /* 找到要掛載的Dom節點 */
    this.parentElement = document.querySelector(elementSelector) || document.body;
    this.options = options;
    this.defaultText = defaultText;

    /* 下拉框的顯示與隱藏狀態 */
    this.downStatus = false;
    /* 下拉框默認選中的值 */
    this.defaultValue = '';
    this._createElement();
},
  • 創建元素節點
 /* 創建Dom節點 */
_createElement() {
    /* 創建下拉框最外層 */
    let dropDown = document.createElement('div');
    dropDown.className = 'dropDown';

    /* 已選中的選項值 */
    let selectedOption = document.createElement('div');
    selectedOption.className = 'selectedOption';

    /* 選中的值 */
    let selectedValue = document.createElement('span');
    selectedValue.className = 'selectedValue';
    /* 先賦值爲默認值 */
    selectedValue.innerText = this.defaultText;

    /* 向下的圖標 */
    let downIcon = document.createElement('i');
    downIcon.className = 'arrowDown';

    /* 將已選中的值的層添加到Dom節點中 */
    selectedOption.appendChild(selectedValue);
    selectedOption.appendChild(downIcon);

    /* 創建選項的外層容器 */
    let optionsContainer = document.createElement('div');
    optionsContainer.className = 'optionsContainer';

    /* 用ul來包含選項層 */
    let ulOptionsList = document.createElement('ul');
    ulOptionsList.className = 'ulOptionsList';

    /* 循環創建每個選項 */
    this.options.forEach((item) => {
        let optionsItem = document.createElement('li');

        /* 是否是選中狀態 */
        if(item.name == this.defaultText) {
            optionsItem.className = 'optionsItem itemSelected';
        } else {
            optionsItem.className = 'optionsItem';
        }
        optionsItem.innerText = item.name;
        ulOptionsList.appendChild(optionsItem);
    });

    /* 添加到每個對應的元素裏面 */
    optionsContainer.appendChild(ulOptionsList);
    dropDown.appendChild(selectedOption);
    dropDown.appendChild(optionsContainer);
    this.parentElement.appendChild(dropDown);

    /* 設置Dom元素,掛載、綁定事件 */
    /* 已選中的選項的包含層 */
    this.selectedOption = selectedOption;
    /* 選中的值 */
    this.selectedValue = selectedValue;
    /* 下拉框選項包含層 */
    this.optionsContainer = optionsContainer;
    this._handleShowOptions(this.parentElement);

    this._unifyWidth(selectedOption);
},
  • 顯示與隱藏相關事件
/* 顯示與隱藏事件 */
_handleShowOptions(element) {
    element.addEventListener('click', (e) => {
        let clickNode = e.target;

        this._unifyWidth(this.selectedOption);

        /* 點擊的是否是下拉框 */
        if(this._isOptionNode(clickNode, this.selectedOption)) {
            if(this.downStatus) {
                this._hiddenDropDown();
            } else {
                this._showDropDown();
            }
        } else if(clickNode.className == 'optionsItem') {
            this._handleSelected(clickNode);
        } else {
            this._hiddenDropDown();
        }
    })
},
/* 判斷是否是下拉框選項 */
_isOptionNode(clickNode, target) {
    if (!clickNode || clickNode === document) return false;
    return clickNode === target ? true : this._isOptionNode(clickNode.parentNode, target);
},
/* 顯示下拉框選項 */
_showDropDown() {
    this.optionsContainer.style.transform = 'scale(1, 1)';
    this.optionsContainer.style.opacity = '1';
    this.selectedOption.className = 'selectedOption';
    this.downStatus = true;
},
/* 隱藏下拉框選項 */
_hiddenDropDown() {
    this.optionsContainer.style.transform = 'scale(1, 0)';
    this.optionsContainer.style.opacity = '0';
    this.selectedOption.className = 'selectedOption';
    this.downStatus = false;
},
  • 定義點擊事件
  /* 對每個選項的點擊事件 */
_handleSelected(clickNode) {
    this.selectedValue.innerText = clickNode.innerText;
    clickNode.className = 'optionsItem itemSelected';
    this._siblingsDom(clickNode, function(clickNode) {
        if(clickNode) {
            clickNode.className = 'optionsItem';
        }
    });
    this._hiddenDropDown();
},

/* 兄弟節點處理函數 */
_siblingsDom(clickNode, callback) {

    /* arguments 是一個對應於傳遞給函數的參數的類數組對象
    * arguments對象是所有(非箭頭)函數中都可用的局部變量
    * 包含傳遞給函數的每個參數,第一個參數在索引0處
    * arguments對象不是一個 Array,它類似於Array,
    * 但除了length屬性和索引元素之外沒有任何Array屬性
    * */

    (function (ele) {
        /* arguments.callee
         * 指向當前執行的函數
         * */
        callback(ele);
        if (ele && ele.previousSibling) {
            arguments.callee(ele.previousSibling);
        }
    })(clickNode.previousSibling);

    (function (ele) {
        callback(ele);
        if (ele && ele.nextSibling) {
            arguments.callee(ele.nextSibling);
        }
    })(clickNode.nextSibling);
},
  • 判斷寬度
/* 判斷寬度 */
_unifyWidth(selectedOption) {
    /* 找到所有的li標籤 */
    let optionsItem = document.querySelectorAll('.optionsItem');
    let standardWidth = selectedOption.offsetWidth;

    /* 對每個li標籤設置寬度 */
    optionsItem.forEach((item) => {
        standardWidth = item.offsetWidth > standardWidth ? item.offsetWidth : standardWidth;
        item.style.width = standardWidth - 32 + 'px';
        selectedOption.style.width = standardWidth + 'px';
    });
}
(function() {

    /* 定義selector下拉框 */
    let Selector = function(params) {
        /* 初始化 */
        this._initSelector(params);
    };

    Selector.prototype = {
        /* 將上面的方法全部放在Selector原型上 */
    };

    /* 掛載到window上*/
    window.$Selector = Selector;
})();
關於原型與原型鏈,可以查看我記錄的js面試
  • 使用js生成的話,相對來說,代碼長點,我這裏的話,對寬度有做判斷,但是不完美
  • 關於DOMContentLoaded可以查看這篇文章DOMContentLoaded
正在努力學習中,若對你的學習有幫助,留下你的印記唄(點個贊咯^_^)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章