jquery를 이용하여 select의 option을 정렬시키는 동작을 아래와 같이 구현하였다.


<select id="sel">
    <option value="0">호랑이</option>
    <option value="1">크레용</option>
    <option value="2">와인</option>
    <option value="3">세공</option>
    <option value="4" selected>셀렉티드</option>
    <option value="5">케이크</option>
    <option value="6">두덕리</option>
    <option value="7">초장</option>    
</select>

<script>
// 정렬함수
function sortSelect(selId)
{
    var sel = $('#'+selId);
    var optionList = sel.find('option');

    optionList.sort(function(a, b){
        if (a.text > b.text) return 1;
        else if (a.text < b.text) return -1;
        else {
            if (a.value > b.value) return 1;
            else if (a.value < b.value) return -1;
            else return 0;
        }
    });

    sel.html(optionList);
}

$(document).ready(function(){
    sortSelect('sel');
});
</script>



이게 다른데서는 잘 동작하는데 IE8 이하에서는 selected 속성이 꼬이기 시작하는 것이다.


디버깅을 해보니 jquery의 html()의 문제였다.

정확히는 html() 내부에서 html을 덮어씌우기 전에 엘리먼트의 내부를 비우기 위해 호출하는 empty()의 문제.


empty() 내부를 보면 아래처럼 firstChild가 없어질 때까지 removeChild()를 해주는 부분이 있다.


// ...
// empty() 내부

while ( elem.firstChild ) {
    elem.removeChild( elem.firstChild );
}

// ...



이게 IE8 이하에서 select의 option을 지우는 것으로 동작할 때,

selected 속성을 가진 option이 지워지면 자동으로 그 다음 option이 selected가 되어버린다.

(쓸데 없는 짓을...)


sel.html(optionList); 가 호출될 때,

empty()를 거치면서 select 엘리먼트의 child로써의 option은 사라지지만, 

option 엘리먼트 그 자체는 살아남아 있다가 정렬된 채로 select 엘리먼트의 child로 들어가는데, 

이때 바로 앞의 selected option이 사라지는 바람에 획득했던 selected가 그대로 남아 selected가 꼬이도록 만드는 것이다.


jquery core의 empty() 를 좀 수정해주면 문제는 해결되나 이게 언제 반영될지, 반영되긴 될지도 모르는 문제이므로,

그냥 아래처럼 기존 option을 그대로 이용하지 않고 새로 만들어서 교체해주는 게 가장 나은 듯 하다.


function sortSelect(selId)
{
    var sel = $('#'+selId);
    var optionList = sel.find('option');

    optionList.sort(function(a, b){
        if (a.text > b.text) return 1;
        else if (a.text < b.text) return -1;
        else {
            if (a.value > b.value) return 1;
            else if (a.value < b.value) return -1;
            else return 0;
        }
    });

    // 정렬된 option 리스트를 HTML로 재작성
    var sorted = '';
    for (var i=0; i<optionList.length; i++) {
        var selected = '';
        if (optionList[i].selected) selected = ' selected';

        sorted += '<option value="'+optionList[i].value+'"'+selected+'>'+optionList[i].text+'</option>';
    }

    sel.html(sorted);
}




혹시나 궁금한 사람이 있을까봐 jquery core의 empty() 수정버전도 함께 남긴다.

만약 empty() 처리할 엘리먼트가 select 라면 selectedIndex를 미리 구해놓고,

option 삭제할 때마다 해당 option의 index가 selectedIndex가 아니라면 selected=false 처리를 해주는 방식이다.

내가 작성한 코드는 한글주석이 달린 부분...; 


    empty: function() {
        var elem,
            i = 0;

        for ( ; (elem = this[i]) != null; i++ ) {
            // Remove element nodes and prevent memory leaks
            if ( elem.nodeType === 1 ) {
                jQuery.cleanData( getAll( elem, false ) );
            }

            // IE8 이하에서 select:option의 selected가 자동으로 추가되는 부분을 위해 추가한 코드
            var isSelect = elem.options && jQuery.nodeName( elem, "select" ),
                selectedIdx = isSelect ? elem.selectedIndex : null,
                optionIndex = 0,
                noSelectedOption = null;

            // Remove any remaining nodes
            while ( elem.firstChild ) {
                // IE8 이하에서 select:option의 selected가 자동으로 추가되는 부분을 위해 추가한 코드
                if (isSelect && selectedIdx !== null && elem.firstChild.nodeName.toLowerCase() === 'option') {
                    if (optionIndex != selectedIdx) noSelectedOption = elem.firstChild;
                    ++optionIndex;
                }

                elem.removeChild( elem.firstChild );

                // IE8 이하에서 select:option의 selected가 자동으로 추가되는 부분을 위해 추가한 코드
                if (noSelectedOption) {
                    noSelectedOption.selected = false;
                    noSelectedOption = null;
                }
            }

            // If this is a select, ensure that it displays empty (#12336)
            // Support: IE<9
            if ( isSelect ) {
                elem.options.length = 0;
            }
        }
    
        return this;
    },












Posted by bloodguy
,