A LIST Apart: For People Who Make Websites

이제 내가 보이나요?

by Aaron Gustafson

자바스크립트로 페이지를 조작하는 일 중 가장 자주 반복하는 일은 아마 내용을 숨겼다, 보였다 하는 패턴일 겁니다. 탭 인터페이스, 접을 수 있는 요소, 아코디언 위젯.역주 거의 모든 곳에서 자바스크립트로 내용을 숨겼다, 보였다 합니다. 이 패턴 자체는 나쁜 점이 아니지만, 내용을 어떻게 숨기느냐에 따라 스크린 리더 같은 보조 기술과 연관된 접근성에 매우 밀접한 영향이 있음을 아는 사람은 많지 않습니다.

Perhaps the most heavily-repeated pattern in JavaScript-based page manipulation is showing and hiding content. Tabbed interfaces. Collapsible elements. Accordion widgets. It crops up nearly everywhere. In and of itself, this pattern is not a bad thing, but few people realize how profoundly your choice of hiding mechanism can influence the accessibility of your content to assistive technologies like screen readers.

자바스크립트 기반 위젯을 직접 만들 때는 요소를 숨기는 방법을 마음대로 제어할 수 있지만, jQuery나 Scriptaculous 같은 애니메이션 라이브러리를 쓸 때는 라이브러리가 요소를 숨기므로 내용의 접근성을 위해 당신이 할 수 있는 일은 별로 없습니다. 바로 이 점이 문제입니다.

When building custom JavaScript-based widgets, it’s quite easy to fully control the hiding mechanism, but when you begin working with animation libraries like jQuery or Scriptaculous, the hiding mechanism is typically dictated by the library, leaving you little control over the accessibility of your content. And that’s a problem.

까꿍~

아래 표에 요약한 것처럼 내용을 숨기는 방법은 여러 가지가 있고 각 방법은 페이지에 다른 영향을 끼칩니다.

When it comes to hiding content, there are several mechanisms for doing it and each affects the page differently, as summarized in the table below:

내용을 숨기는 방법역주

CSS 규칙

화면 효과

접근성 효과

visibility:hidden;

요소는 눈에 보이지는 않지만 레이아웃에는 남아 있습니다 (여전히 공간을 차지합니다).

Element is hidden from view, but is not removed from the normal flow (i.e., it still takes up the space it normally would)

스크린 리더는 해당 내용을 무시합니다.

Content is ignored by screen readers

display:none;

요소는 레이아웃에서 제거됩니다. 요소가 차지했던 공간도 제거됩니다.

Element is removed from the normal flow and hidden; the space it occupied is collapsed

스크린 리더는 해당 내용을 무시합니다.

Content is ignored by screen readers

height:0; width:0; overflow:hidden;

요소는 접히고 내용은 숨겨집니다.

Element is collapsed and contents are hidden

스크린 리더는 해당 내용을 무시합니다.

Content is ignored by screen readers

text-indent:-999em;

내용은 화면 밖으로 밀려나지만, 링크에 포커스가 걸릴 때 등은 어색할 수 있으며 음수 들여쓰기가 내용을 완전히 숨기기에는 충분치 않을 수 있습니다.

Contents are shifted off-screen and hidden from view, but links may “focus” oddly and negative indent may not prove long enough to fully hide content

스크린 리더가 내용에 접근할 수 있지만 텍스트와 인라인 요소일 때만 가능합니다.

Screen readers have access to the content, but the content is limited to text and inline elements

position:absolute; left:-999em;

내용은 레이아웃에서 제거되고 왼쪽 경계선 밖으로 밀려납니다. 내용이 차지했던 공간은 접힙니다.

Content is removed from the normal flow and shifted off the left-hand edge; the space it occupied is collapsed

스크린 리더가 내용에 접근할 수 있습니다.

Screen readers have access to the content

앞의 두 방법이 아마도 가장 많이 쓰일 것이며, 특히 거의 모든 자바스크립트 라이브러리와 위젯에서 display:none;을 씁니다. 스크린 리더도 숨긴 내용에 접근하지 못하게 하려면 display:none;를 쓴 라이브러리나 위젯도 괜찮지만, 사용자들이 숨겨진 내용에도 접근할 수 있게(화면에는 보이지 않더라도) 하려면 내용을 화면 밖으로 보내는 마지막 방법이 유일한 해답입니다.

The first two mechanisms are probably the most popular, with display: none; being the go-to option implemented by nearly every JavaScript library on the planet and the lion’s share of ready-made JavaScript widgets. If you don’t want your hidden content to be read by a screen reader, those defaults may work for you, but if you want to ensure users have access to content (even if it isn’t displayed visually in the current interface), the final option (positioning content offscreen) is really the way to go.

참고: 여기서 설명하는 방법은 왼쪽에서 오른쪽으로 쓰는 언어를 기준으로 합니다. 오른쪽에서 왼쪽으로 쓰는 언어에 적용하려면 left 오프셋 대신 right를 쓰십시오.

Note: the offscreen positioning assumes a left-to-right language page. For right-to-left, swap a right offset for the left one.

스크립트

자바스크립트 라이브러리를 직접 만들어 쓴다면 요소에 position속성을 적용해 화면 밖으로 밀어내기는 무척 쉽습니다. 하지만 jQuery나 Prototype 같은 라이브러리를 쓴다면 라이브러리 내부를 덮어쓰거나 다른 방법으로 바꿔야 하므로 매우 어렵습니다.

If you roll your own JavaScript library, positioning content off-screen to hide it is pretty easy to implement. If, however, you are using a third-party JavaScript library, such as jQuery or Prototype, this task becomes much more difficult to accomplish because making the change requires overwriting or otherwise changing the internals of the library. Unless, of course, you’re smart about how you do it.

라이브러리 대부분은 "콜백 함수"라 불리는 것을 넘겨받는 메커니즘을 애니메이션 함수의 일부로 포함하고 있습니다. 콜백 함수란 다른 함수(또는 객체 메서드)에 전달해서 미리 지정한 시간에 호출하는 함수를 말합니다. Ajax를 통해 컨텐츠를 불러와 본 일이 있다면 이 개념에 익숙할 겁니다. 이런 패턴에서는 서버에서 가져온 데이터를 조작하는 함수가 콜백 함수입니다.

Most libraries include, as part of their animation suite, a mechanism for including what are referred to as “callback functions.” A callback function is a function that you supply to another function (or object method) so that it can be called at a predetermined time. If you’ve used JavaScript to load content via Ajax, you’re probably familiar with the concept: callback functions are used to do something with the data you got back from the server.

자바스크립트 라이브러리 대부분은 주어진 행동이 끝났을 때만 콜백 함수를 호출할 수 있지만, 일부 라이브러리는 주어진 행동 중 여러 시점에서 콜백 함수를 호출할 수 있는데 예를 들어 주어진 행동이 시작하자마자 콜백 함수를 호출할 수도 있습니다. 하지만 라이브러리에서 콜백 함수를 호출할 시점을 마련해 주지 않았더라도 숨기기/보이기 작업을 좀 더 접근성 있게 만들 수 있습니다. 예를 들어 jQuery 기반 예제를 봅시다.

In most cases, JavaScript libraries only offer a callback function that runs at the completion of a given activity, but some libraries also provide hooks for various other points during the execution of a given routine, such as before the routine begins. Even without additional callback hooks, however, it’s possible to create more accessible show/hide operations. Take the following jQuery-based snippet, for example:

(function(){
    var $button = $('#myButton'),
        $text   = $('#myText'),
        visible = true;

    $button.click(function(){
        if (visible) {
            $text.slideUp('fast');
        } else {
            $text.slideDown('fast');
        }
        visible = !visible;
    });
})();

이 스크립트는 요소 두 개(#myButton#myText)를 찾아서 지역 변수(각각 $button$text)에 저장한 후 현 상황을 추적할 세 번째 지역 변수(visible)를 설정합니다. 다음으로 #myText의 높이를 조절해서 보였다 숨겼다 할 함수를 #myButtononclick이벤트 핸들러로 등록합니다. 아주 간단하죠?

This script finds two elements (#myButton and #myText), assigning them to two local variables ($button and $text, respectively) before setting a third local variable (visible) to track the current state of things. It then goes on to assign an onclick event handler to #myButton that toggles the visibility of #myText by adjusting its height. Pretty straightforward, right?

이 스크립트는 기대한 대로 동작합니다만, 현재 jQuery는 slideUp()를 호출할 때 display: none을 쓰므로 #myText를 숨기면 스크린 리더에서 접근할 수 없습니다. 하지만 요소를 좀 더 접근성 있는 방법으로 숨길 class를 사용하도록 코드를 고칠 수 있습니다.

This script works as you’d expect, but jQuery currently uses display: none when you call slideUp(), so #myText is being hidden via a method that prohibits the hidden text from being read by a screen reader. By making a subtle tweak to the code, however, we can trigger the addition of a class we control that provides for a more accessible means of hiding content:

(function(){
    var $button = $('#myButton'),
        $text   = $('#myText'),
        visible = true;

    $button.click(function(){
        if (visible) {
            $text.slideUp('fast', function(){
                $text.addClass('accessibly-hidden')
                     .slideDown(0);
            });
        } else {
            $text.slideUp(0, function(){
                $text.removeClass('accessibly-hidden')
                     .slideDown('fast');
            });
        }

        visible = !visible;
    });
})();

이 스크립트는 내용을 숨기는 과정에서는 라이브러리가 애니메이션을 제어하므로 앞 예제와 거의 똑같지만, 애니메이션이 끝난 다음 기본 상태 대신 우리가 직접 만든 “accessibly-hidden” 클래스를 적용하므로 보조 장치에서 숨겨진 내용에 접근할 수 있습니다. 숨긴 내용을 다시 보여줄 때는 순서를 뒤집어서 클래스를 제거한 후 애니메이션이 일어나게 합니다.

This script is almost identical to the last one, in that when the content is being hidden, the library is allowed to manage the animation, but then the script swaps the default completion state for our custom class “accessibly-hidden,” thereby keeping the content available to assistive devices. When the script goes to show the content, the steps are reversed, with the content being hidden by the script again before the class is removed and the actual animation is performed.

이 접근법의 또 다른 장점은 내용을 숨기는 방법을 자바스크립트 라이브러리에 맡기지 않고 완전히 제어한다는 점입니다. 더 좋은 방법을 알게 된다면 라이브러리에서 요소를 숨기는 메커니즘을 업그레이드할 때까지 기다리지 않고 “accessibly-hidden”를 바꿔서 그 방법을 쓸 수 있습니다.

The added benefit of this approach is that you control the method of hiding content completely, as opposed to leaving it up to the JavaScript library. That means you can upgrade your “accessibly-hidden” to use a different technique if something better comes along and you don’t have to wait for the library to upgrade its hiding mechanism (if it ever does).

자바스크립트는 상호작용성이 풍부한 온라인 애플리케이션을 만들 수 있는 강력한 도구입니다. 우리는 자바스크립트를 쓸 때 거슬리지 않게 해야 하며, 동작 계층은 점진적으로 만들어 가야 함을 이미 알고 있지만 그렇게 하기가 항상 쉽지는 않습니다. 다행히 생각을 좀 더 하면 스크립트가 약간의 단계를 더 거치게, 페이지를 좀 더 접근성있게 만들 수 있습니다.

JavaScript is a powerful tool for building rich interactions online. We already know that, when we do it, we need to be unobtrusive and build the behavior layer following progressive enhancement, but sometimes that only gets us so far. Thankfully, however, with a little thoughtful reflection, we can take our scripts that extra step and make our pages just a little more accessible.

1. 아코디언 위젯이란, 입력할 내용이 아주 많은 폼에서 사용자가 부담스러워하는 것을 막기 위해 영역을 나눠서 접었다 펼쳤다 하며 입력받는 것을 말합니다. 이에 관한 글을 번역한 일이 있으니 관심있으시면 읽어보세요.돌아가기

2. 국내에서 주로 사용하는 스크린리더인 센스리더와 드림보이스는 visibility:hidden을 읽어준다고 합니다. 이런 현실을 반영하여 2009년부터 웹 접근성 연구소에서 스크린리더를 위해 display:none보다는 visibility:hidden을 이용하자는 캠페인을 하고 있다고 합니다. 물론 여러 보조기기의 구현 정도는 제품이나 버전 별로 다를 수 있으니 실무에 적용하려면 사전 테스트를 해야 합니다.돌아가기

댓글 (6)

저자

 Aaron GustafsonAaron Gustafson is the founder and principal consultant of Easy! Designs, a web development boutique. He is also a manager of the Web Standards Project (WaSP) where he helped found Web Standards Sherpa. He authored Adaptive Web Design, and serves as a technical editor here at ALA