1. 程式人生 > >自定義input[type="radio"]的樣式

自定義input[type="radio"]的樣式

fine .com 簡單 表單元 edge 1.4 外邊距 ren none

對於表單,input[type="radio"] 的樣式總是不那麽友好,在不同的瀏覽器中表現不一。

2017年11月28日更新

對單選按鈕自定義樣式,我們以前一直用的腳本來實現,不過現在可以使用新的偽類 :checkbox 來實現。

如果直接對單選按鈕設置樣式,那麽這個偽類並不實用,因為沒有多少樣式能夠對單選按鈕起作用。不過,倒是可以基於單選按鈕的勾選狀態借助組合選擇符來給其他元素設置樣式。

很多時候,無論是為了表單元素統一,還是為了用戶體驗良好,我們都會選擇 label 元素和 input[type="radio"] 一起使用。當<label>元素與單選按鈕關聯之後,也可以起到觸發開關的作用

思路:

1. 可以為<label>元素添加生成性內容(偽元素),並基於單選按鈕的狀態來為其設置樣式;

2. 然後把真正的單選按鈕隱藏起來;

3. 最後把生成內容美化一下。

解決方法:

1. 一段簡單的結構代碼:

技術分享圖片
<div class="female">
    <input type="radio" id="female" name="sex" />
    <label for="female">女</label>
</div>
<div class="male">                
    <input type="radio" id="male" name="sex" />
    <label for="male">男</label>
</div>

2. 生成一個偽元素,作為美化版的單選按鈕,先給偽元素添加一些樣式:

技術分享圖片
input[type="radio"] + label::before {
    content: "\a0"; /*不換行空格*/
    display: inline-block;
    vertical-align: middle;
    font-size: 18px;
    width: 1em;
    height: 1em;
    margin-right: .4em;
    border-radius: 50%;
    border: 1px solid #01cd78;
    text-indent: .15em;
    line-height: 1; 
}

現在的樣子:

技術分享圖片

原來的單選按鈕仍然可見,但是我們先給單選按鈕的勾選狀態添加樣式:

3. 給單選按鈕的勾選狀態添加不同的樣式:

input[type="radio"]:checked + label::before {
    background-color: #01cd78;
    background-clip: content-box;
    padding: .2em;
}

現在的樣子:

技術分享圖片

技術分享圖片

4. 現在把原來的單選按鈕隱藏:

input[type="radio"] {
    position: absolute;
    clip: rect(0, 0, 0, 0);
}

現在的樣子:

技術分享圖片

技術分享圖片

隱藏原來的單選按鈕時,如果使用 display: none; 的話,那樣會把它從鍵盤 tab 鍵切換焦點的隊列中完全刪除。

於是可采用剪切的方式,讓剪切後的尺寸為零,這樣就隱藏了原來的單選按鈕。


下面為舊內容:

為了最大程度的顯示出它們的差別,並且為了好看,首先定義了一些樣式:

技術分享圖片
<form action="">
    <div class="sex">
        <div class="female">
            <label for="female">女</label>
            <input type="radio" name="sex" id="female">
        </div>
        <div class="male">
            <label for="male">男</label>
            <input type="radio" name="sex" id="male">
        </div>
    </div>
</form>
技術分享圖片
body { margin: 0; }
input { padding: 0; margin: 0; border: 0; }
.female, .male {
    position: relative;
    height: 40px;
    line-height: 40px;
    margin-left: 40px;
}
.sex label {
    display: block;
    height: 40px;
    width: 40px;
    line-height: 40px;
    font-size: 20px;
    cursor: pointer;
}
.sex input {
    z-index: 3;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 40px;
    margin: auto;
    display: block;
    width: 30px;
    height: 30px;
    cursor: pointer;
}

然後在各個瀏覽器中觀察,會發現有很大的差別:

ie:

技術分享圖片

edge:

技術分享圖片

opera:

技術分享圖片

chrome:

技術分享圖片

firefox:

技術分享圖片

對於 firefox 瀏覽器,即便是設置了寬和高,依然是沒有效果,input[type="radio"] 的那個圓圈還是初始狀態那麽大。其它瀏覽器的表現也不一致,為了達到一致的效果,我們需要做兼容處理。

思路:

1. 將 input[type="radio"] 隱藏, opacity: 0; 置於上層,當我們點擊它時,就能正確的響應原本的事件。

2. 自定義一個圓圈,置於下層,模擬原本相似的樣式;

3. 用 js 實現選中 input[type="radio"] 時,在其下層的自定義的元素改變原來的背景顏色。

代碼:

技術分享圖片
<form action="">
    <div class="sex">
        <div class="female">
            <label for="female">女</label>
            <input type="radio" name="sex" id="female">
            <span class="female-custom"></span> <!-- 同下面的 span 一樣作為自定義的元素 -->
        </div>
        <div class="male">
            <label for="male">男</label>
            <input type="radio" name="sex" id="male">
            <span class="male-custom"></span>    
        </div>
    </div>
</form>
技術分享圖片
body { margin: 0; }
input { padding: 0; margin: 0; border: 0; }
.female, .male {
    position: relative; /* 設置為相對定位,以便讓子元素能絕對定位 */
    height: 40px;
    line-height: 40px;
    margin-left: 40px;
}
.sex label {
    display: block;
    height: 40px;
    width: 40px;
    line-height: 40px;
    font-size: 20px;
    cursor: pointer;
}
.sex input {
    z-index: 3;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 40px;
    margin: auto; /* 這裏及以上的定位,可以讓該元素豎直居中。(top: 0; bottom: 0;) */
    opacity: 0;
    display: block;
    width: 30px;
    height: 30px;
    cursor: pointer;
}
.sex span {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 40px;
    margin: auto;
    display: block;
    width: 25px;
    height: 25px;
    border: 1px solid #000;
    border-radius: 50%;
    cursor: pointer;
}        
.sex span.active {
    background-color: #000;            
}
技術分享圖片
$("#male").click( function () {
    $(this).siblings("span").addClass("active");
    $(this).parents("div").siblings("div").children("span").removeClass("active");
});
$("#female").click( function () {
    $(this).siblings("span").addClass("active");
    $(this).parents("div").siblings("div").children("span").removeClass("active");
});

這樣處理後,在瀏覽器中展示效果全部一樣了:

技術分享圖片技術分享圖片技術分享圖片

擴展:

1. 對於代碼中出現的定位,對父元素使用 position: relative; 給子元素使用 position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; 能實現讓子元素相對於父元素居中(滿足水平居中和豎直居中)顯示。如果只是需要豎直居中,則不需要添加 right: 0; 和 left: 0; 的樣式。

2. 有時當我們不容易確定子元素的高度時,可以這樣設置:對父元素 position: relative; 對子元素 position: absolute; top: 10px; bottom: 10px; margin: auto; 這樣一來,子元素的高度就是父元素的高度減去20px後的值了,同樣,top 和 bottom 支持百分數,可擴展性更強。

優化更新:

需求:

1. 有時候我們需要內聯的單選樣式;

2. 選中的按鈕內的小圓圈不需要占滿整個外圈的大小。

思路:

1. 讓每一個包裹選擇的 div 左浮動;

2. 給父元素添加圓形的外邊框,子元素設置一個稍小於父元素大小的背景。

代碼:

技術分享圖片
<form action="">
    <div class="fruit">
        <div class="apple">
            <label for="apple">蘋果</label>
            <input type="radio" name="fruit" id="apple">
            <div class="user-defined">
                <span class="circle"></span>
            </div>
        </div>
        <div class="banana">
            <label for="banana">香蕉</label>
            <input type="radio" name="fruit" id="banana">
            <div class="user-defined">
                <span class="circle"></span>
            </div>
        </div>
        <div class="orange">
            <label for="orange">橘子</label>
            <input type="radio" name="fruit" id="orange">
            <div class="user-defined">
                <span class="circle"></span>
            </div>
        </div>
    </div>
</form>
技術分享圖片
* { box-sizing: border-box; }

body { padding: 50px; }

input { padding: 0; margin: 0; border: 0; }

.fruit:before { content: ""; display: table; }

.fruit:after { content: ""; display: table; clear: both; }

.fruit > div { position: relative; float: left; margin-right: 50px; width: 80px; height: 40px; line-height: 40px; }

.fruit > div:last-child { margin-right: 0; }

.fruit label { display: block; width: 50px; height: 40px; line-height: 40px; cursor: pointer; }

.fruit input { z-index: 3; display: block; opacity: 0; position: absolute; top: 0; bottom: 0; left: 50px; margin: auto; width: 30px; height: 30px; cursor: pointer; }

.fruit .user-defined { z-index: 2; position: absolute; top: 0; bottom: 0; left: 50px; margin: auto; width: 30px; height: 30px; border: 1px solid #000; border-radius: 50%; cursor: pointer; }

.fruit .user-defined span.circle  { display: block; width: 24px; height: 24px; margin-top: 2px; margin-left: 2px; background-color: transparent; border-radius: 50%; }

.fruit .user-defined span.active  { background-color: #000; }
$("input").click(function() {
    $(this).siblings("div").children("span").addClass("active");
    $(this).parents("div").siblings("div").find("span").removeClass("active");
});

效果顯示如下:

技術分享圖片

優化更新: (2016年11月07日)

需求:

1. 有時候我們的選擇只有兩種,比如性別;

2. 這種選擇希望能像手機系統設置裏的切換一樣。

思路:

1. 讓每一個包裹選擇的 div 左浮動;

2. 讓當前子元素添加區別的背景。

預期結果:

技術分享圖片

代碼:

技術分享圖片
<div class="sex">
    <span class="warning fl">性別確定後將不可更改!</span>
    <div class="select fr">
        <div class="male fl">
            <label for="male">                        
                <input type="radio" name="sex" id="male" checked>男
            </label>
            <span class="btn active">男</span>
        </div>
        <div class="female fl">
            <label for="female">
                <input type="radio" name="sex" id="female">女
            </label>
            <span class="btn">女</span>
        </div>                    
    </div>         
</div>
技術分享圖片
/*性別*/
.sex span.warning {
    font-size: 1.4rem;
    color: #ccc;
}
.sex .male,
.sex .female {
    position: relative;
    width: 4rem;
    height: 3.9rem;
    z-index: 1;
    line-height: 3.9rem;
    text-align: center;
}
.sex .male label,
.sex .female label {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4rem;
    height: 3.9rem;
    z-index: 3;
    opacity: 0;
    margin: auto;
    display: inline-block;
    line-height: 3.9rem;
    cursor: pointer;
}
.sex input {
    display: inline-block;
    vertical-align: middle;/*讓默認的單選樣式的圓圈和“男”“女”的文本沒有高差,看起來在同一水平線*/
    height: 2.8rem;
    line-height: 2.8rem;
    margin: 0; /*清除瀏覽器默認的外邊距*/
}
.sex .male span.btn,
.sex .female span.btn {
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 4rem;
    height: 2.8rem;
    z-index: 2;
    margin: auto;
    display: inline-block;
    line-height: 2.6rem;
    text-align: center;
    border: .1rem solid #fe5454;
    color: #fe5454;
}
.sex .male span {
    border-top-left-radius: .2rem;
    border-bottom-left-radius: .2rem;
}
.sex .female span {
    border-top-right-radius: .2rem;
    border-bottom-right-radius: .2rem;
}
.sex .male span.active,
.sex .female span.active {
    background-color: #fe5454;
    color: #fff;
}    

如果用 jQuery 來寫切換的功能的話,很簡單的幾行代碼:

$(".select label").click(function() {
    $(this).siblings("span").addClass("active");
    $(this).parent().siblings("div").find("span").removeClass("active");
}); 

自定義input[type="radio"]的樣式