menu

페이지 내부의 id를 타겟으로 앵커<a> 태그를 심으면 id를 가지고 있는 요소가 창의 맨 윗부분으로 위치하도록 스크롤이 뿅 하고 이동하게 된다.

바로 거기에서 문제가 시작된다. 이 <a> 태그가 도입된 지 십수 년이 되도록 단 한 명의 브라우저 개발자도 스무드 스크롤링 등의 기능을 적용해주지 않았던 것이다. 분노를 참지 못한 나는 결국 직접 코딩을 시작하는데...

#1 문제 파악

문제점은 크게 세 가지가 있다.

  1. 앵커가 문서 내부에 있는 곳을 가리키는지, 아니면 외부를 가리키는지 구분되지 않음.
  2. 링크 누름 → 해당 요소로 갑자기 뿅! → 누른 사람들 어리둥절.
  3. 링크 누름 → 해당 요소가 꼭대기에 뙇! → 블로그 상단 정보창이 내려와서 요소 가림.

#2 문제 해결

#2-1

일단 문서 내부를 가리키는 앵커일 경우 아이콘으로 표시. css에 몇 줄 추가해주면 해결!

.article a[href^="#"]:before    { font: 1.333em 'icon'; content: "\f293"; }
.article a:not([href*="jinh.tistory.com"]):not([href^="#"]):not([href^="/"]):before    { font: 1em 'icon'; content: "\f35b"; }

그리고 Material Design IconsMaterial Design Iconic Font 등 웹폰트에다 아이콘 박아넣은 개꿀 라이브러리 줍줍 후 치덕치덕 발라줌.

그럼 이렇게 나옴: [문서 내부], [블로그 내부], [블로그 외부] 이야 멋스럽다!

#2-2, 2-3

여기서 문서 내부로 이동하는 링크를 발견한 사람들의 심리 상태를 묘사해본다.

(시작)

[링크 발견]

['아앗 페이지 내부의 유용한 곳으로 이동하는 링크가??']

<클릭>

[문서 내부의 어딘가로 순간 이동]

['띠용! 여기가 어디지 내가 뭘 눌렀더라???']

[혼란, 공황, 두려움]

[잠시 후 냉정 찾음]

['아 내가 찾는 내용이 창의 최상단에 있었구나!']

(끝)

흠... 이 과정을 아래에 묘사한 간결한 의식의 흐름으로 바꾸는 것이 목표.

(시작)

[링크 발견]

['아앗 페이지 내부의 유용한 곳으로 이동하는 링크가??']

<클릭>

['아앗 문서 내부의 어딘가로 이동하... 호옹이! 이렇게 유용한 내용으로 연결되다니!']

(끝)

일단, 부드럽게 스크롤을 이동시키는 코드를 추가.

function scroll_smooth() { 
    $("a[href^='#']").click(function(event){
        event.preventDefault();                                           //기본으로 실행되는 기능을 막고,
        $('body').animate({scrollTop:$(this.hash).offset().top}, 500);    //jquery animate로 스크롤 이동
    }
}
$(function() {    //문서 로딩이 끝나면 실행
    scroll_smooth();
});

위 코드는 성공적으로 동작했다. 그러나 내 블로그는 스크롤을 어느 정도 올리면 상단에 정보창이 튀어나오게 되어있는데, 이동된 요소를 덮어버리기 때문에 어리둥절한 사용자가 호흡곤란 증세를 나타내다가 결국 다이어트 계획마저 잊어버리고 야식으로 치킨을 시켜먹기로 결정해버리는 안타까운 상황까지 발전할 수도 있다.

그래서 코드는 다음과 같이 수정되었다.

function scroll_smooth() { 
    $("a[href^='#']").click(function(event){
        event.preventDefault();
        if($(this.hash).offset().top < $('body').scrollTop() - 600){                                 //타켓 위치가 현 스크롤 위치보다 높으면
            $('body').animate({scrollTop:$(this.hash).offset().top - $('header').height()}, 500);    //헤더만큼 밑으로 내려줌
        } else ​{
            $('body').animate({scrollTop:$(this.hash).offset().top}, 500);
    }
}//이하 생략

막상 만들고 나니깐, 애초에 요소를 창 맨 꼭대기에다 표시할 이유가 전혀 없다는 생각이 들었다. 그냥 마우스 포인터 있는 자리로 이동시키면 되잖아? 그래서 또 수정.

function scroll_smooth() { 
    $("a[href^='#']").click(function(event){
        event.preventDefault();
        if($(this.hash).offset().top < $('body').scrollTop() - 600 && event.pageY - $('body').scrollTop() < $('header').height()){
            $('body').animate({scrollTop:$(this.hash).offset().top - $('header').height()}, 500);    //얘는 예외로 두고
        } else ​{                                                                                     //아랫놈을 마우스 포인터 위치로 이동시키도록 함
            $('body').animate({scrollTop:$(this.hash).offset().top - event.pageY + $('body').scrollTop() + $(this.hash).height() / 2}, 500)
    }
}//이하 생략

완성된 코드 돌려보니 개멋있음... (짝짝짝)

근데... 뭔가 부족함을 느낀 나는 결국... 타겟 요소를 색상으로 강조해줘야겠다는 아이디어를 생각해 내고야 말았다.

        function bg_change(color, time){
                $(this.hash).animate({"background-color":color}, time);
        }

이런 함수를 만들긴 했는데, 여기서 또 문제 발생! 문제를 하나 해결하면 다른 문제가 두 개 발생한다는 코딩의 법칙 몸소 체험 중...

문제 1: jQuery로는 배경색을 애니메이션으로 못 만든다. → jQuery UI 혹은 css의 transition 값으로 해결.

문제 2: <table> 의 자식 요소인 <tr> 태그는 background-color 가 적용되지 않는다. → 태그 이름 읽어서 tr일 때 예외 적용.

어찌어찌 해서 완성된 코드는 다음과 같다.

function scroll_smooth() {
    $("a[href^='#']").click(function(event){
        event.preventDefault();
        
        var target = $(this.hash);
        var target_bg;
        if (target[0].tagName == "TR"){
            target_bg = $(this.hash).children("td").css("background-color");
        } else {
            target_bg = $(this.hash).css("background-color");
        }
        function bg_change(color, time){
            if (target[0].tagName == "TR"){
                target.children("td").css({"background-color":color,"transition":time});
            }else{
                target.css({"background-color":color,"transition":time});
            }
        }
        
        if($(this.hash).offset().top < $('body').scrollTop() - 600 && event.pageY - $('body').scrollTop() < $('header').height()){
            $('body').animate({scrollTop:$(this.hash).offset().top - $('header').height()}, 500, function(){bg_change(target_bg,".75s");});
        }else{
            $('body').animate({scrollTop:$(this.hash).offset().top - event.pageY + $('body').scrollTop() + $(this.hash).height() / 2}, 500, function() {bg_change(target_bg,".75s");});
        }
        
        bg_change("#ffeb3b",".25s");
    });
}

 

#3 결과

테스트 앵커를 눌러보자. 혹은, 위 코드가 적극적으로 적용된 페이지 "여초 용어 정리" 게시물에서 확인하자. 

끝.

붙임 #1: 추가 사항

코드가 파이어폭스에서 안 된다는 댓글을 보고 기분이 다운됐었지만, 해결 후 다시 기분 좋아짐 ㅎㅎ. 게다가 위치 이동 후 더블 클릭하면 다시 그 앵커 자리로 되돌아가는 코드까지 추가 시킴. 완성된 코드는 아래에...

function scroll_smooth() {
    $("a[href^='#']").click(function(event){
        event.preventDefault();
        
        var target = $(this.hash);
        var target_reverse = $(this);
        var target_bg;
        var reversible = true;
        
        function bg_change(t, color, time){
            if (t[0].tagName == "TR"){
                t.children("td").css({"background-color":color,"transition":time});
            }else{
                t.css({"background-color":color,"transition":time});
            }
        }
        
        if (document.height === null) {
            pageYOffset = document.documentElement.scrollTop;
        }
        function scroll(target, event){
            if (target[0].tagName == "TR"){
                target_bg = target.children("td").css("background-color");
            } else {
                target_bg = target.css("background-color");
            }
            if(target.offset().top < pageYOffset - 600 && event.pageY - pageYOffset < $('header').height()){
                target_position = target.offset().top - $('header').height();
            }else{
                target_position = target.offset().top - event.pageY + pageYOffset+ target.height() / 2;
            }
            $('html, body').animate({scrollTop:target_position}, 500, function() {
                bg_change(target, target_bg,".75s");
            });
        }
        
        scroll(target, event);
        bg_change(target, "#ffeb3b",".25s");
        toast("원래 자리로 가려면 더블클릭");
        
        document.ondblclick = function(event){
            if(reversible){
                scroll(target_reverse, event);
                bg_change(target_reverse, "#ffeb3b",".25s");
                reversible = false;
            }
        };
    });
}

퍼가지 말고, 링크로 공유하세요. 자세한 건 공지에.

퍼가지 마세요...
링크로 공유하세요 ㅠㅠ
부탁할게요 ^_<~*

test 3-1

test 3-2

test 3-3

test 4-1

test 4-2

test 4-3

모든 글 보기
공지
방명록
Share to...

페이스북 공유

트위터 공유

구글+ 공유

카카오스토리 공유

밴드 공유

Follow & Contact

Facebook

Twitter

Mail

RSS 구독

2007-2016 © JinH