해시링크란?

<a> 코드의 href 링크 안에 문서 내 id값을 넣어서 해당 id 위치로 이동하게 하는 해시 링크(Hash Link). 문서 내 주석이나 참조를 쓸 때나 nav list에 많이 쓴다. 내가 DLC 웹사이트 하드코딩 할 때 가장 많이 쓰는 기능 중 하나다.

ex: 문서의 nav list에 쓰인 해시링크

<ul>
    <li><a href="#intro">JavaScript 소개</a></li>
    <li><a href="#specification">표준 명세</a></li>
</ul>

ex: 문서의 주석 역할로 쓰인 해시링크

<p>var, let 정의 키워드를 앞에 둔 변수에 초기값을 지정하지 않았다면 그 변수는 <a href="#nullUndefined">undefined</a> 값을 할당받습니다.</p>

이슈 내용: 해시링크로 이동했을 때 컨텐츠가 너무 위에 붙음

그런데 해시 링크로 이동하게 하면 가끔 의도한 위치에서 조금 벗어난 위치로 스크롤이 멈추는 경우가 많다. 해당 id값이 margin값 등 요소 바깥 여백이 따로 설정된 div에 있더라도, div 내에서 가시적으로 보이는 영역의 최상단 (예를 들어 <h1>)으로 스크롤이 이동한다.

이슈 발생 시 예상되는 문제점

만약 스크린에서 header가 fixed 되어있고, 해당 링크 요소에 여백이 충분히 설정되어있지 않다면, 해시링크로 스크롤이 이동되면 <h1> 등 보여지길 원하는 콘텐츠가 header에 가려져버리는 이슈가 발생한다. 이 이슈가 발생하면 사용자는 스크롤이 이상하게 이동되었다고 느끼고, 원하는 콘텐츠로 정확히 이동되었는지 파악하기 힘들어진다. 해시링크의 제 역할을 다하지 못하는 것이다.

이슈 해결과정

1. 요소의 margin & padding-top: fixed-header의 height값 + @

그렇다면 해시링크가 걸릴 요소들은 header의 height를 고려해서 margin, padding 여백을 충분히 주면 되지 않을까? 난 처음에 요소값에 padding-top을 크게 주어서 해결했다. (margin-top을 주어봤지만 요소 내 여백이 아니기때문에 이슈 해결에 영향을 미치지 않았다.)

그런데 이런 해결방법은 효율적인 해결책은 아니었다.

  1. 기존에 구상했던 페이지 디자인과 크게 어긋났고, 결국 여백값과 전체 타이포그래피를 다시 설계해야했다. 결과적으로 페이지 디자인의 자유도를 크게 해치고, 새로운 타이포그래피를 위한 리디자인을 2시간동안 해야했다.
  2. 그리고 여백을 크게 주다보니 모바일 등 작은 화면으로 봤을 때 컨텐츠가 없는 빈 화면도 종종 보였다.
  3. 미디어쿼리를 사용하지 않았기 때문에 wrapper 레이아웃은 margin: x auto 이지만 font-size 변화는 주지 않았다. 미디어쿼리를 넣으면 달라지겠지만 이 페이지는 적응형보다 컨텐츠 업데이트에 집중하고있다.
  4. 사용자가 지나치게 넓은 여백을 봤을 때 자칫하면 콘텐츠가 비어있거나 페이지가 덜 만들어졌다고 잘못 인식할 수도 있다.

요소의 여백 디자인을 해치지 않으면서 문제를 해결할 수는 없을까?

2. height값을 준 가상 요소 :before 생성

이후 우연히 다른 개발자분의 블로그를 서핑하다가 해당 이슈를 다룬 글을 발견했고, 내가 생각하지 못했던 좋은 해결책을 발견했다. 요소 앞에 가상 선택자 :before을 만들고 거기에 height값을 주는 것이다. 아래에 해당 링크를 적어놓았다. 해쉬 링크 오프셋 조정하기 | mulder 님의 블로그

:target:before{
  content: "";
  display: block;
  height: 2em;          /* fixed header 높이 만큼 부여 or 브라우저 상단에서 띄워놓기 원하는 높이 */
  margin-top: -2em;     /* 위에서 설정한 높이와 동일한 만큼을 음수로 제공 */
  visibility: hidden;
}