Popovers

Popovers (팝오버)

iOS에서 볼 수 있는 것 같은 Bootstrap 팝오버를 사이트의 아무 요소에나 추가하는 문서와 예시를 다룹니다.

출처: 문서

본문

개요 (Overview)

popover 플러그인을 사용할 때 알아둘 것들:

  • 팝오버는 위치 지정을 위해 서드파티 라이브러리인 Popper에 의존합니다. bootstrap.js보다 먼저 popper.min.js를 포함하거나, Popper가 포함된 bootstrap.bundle.min.js 하나를 사용해야 합니다.
  • 팝오버는 popover 플러그인을 의존성으로 요구합니다.
  • 성능상의 이유로 팝오버는 opt-in 방식이므로 직접 초기화해야 합니다.
  • 제로 길이의 title과 content 값은 팝오버를 절대 표시하지 않습니다.
  • 더 복잡한 컴포넌트(입력 그룹, 버튼 그룹 등)에서 렌더링 문제를 피하려면 container: 'body'를 지정하세요.
  • 숨겨진 요소에서 팝오버를 트리거하는 것은 동작하지 않습니다.
  • .disabled 또는 disabled 요소의 팝오버는 래퍼 요소에서 트리거해야 합니다.
  • 여러 줄에 걸쳐 감싸진 앵커에서 트리거할 때 팝오버는 앵커들의 전체 너비 사이에서 가운데 정렬됩니다. 이 동작을 피하려면 <a>에 .text-nowrap을 사용하세요.
  • 해당 요소들이 DOM에서 제거되기 전에 팝오버를 숨겨야 합니다.
  • shadow DOM 안의 요소 덕분에 팝오버를 트리거할 수 있습니다.

기본적으로 이 컴포넌트는 내장 콘텐츠 살균기(sanitizer)를 사용하며, 명시적으로 허용되지 않은 HTML 요소는 모두 제거합니다. 자세한 내용은 JavaScript 문서의 sanitizer 섹션을 참고하세요.

이 컴포넌트의 애니메이션 효과는 prefers-reduced-motion 미디어 쿼리에 의존합니다. 접근성 문서의 reduced motion 섹션을 참고하세요.

몇 가지 예시로 팝오버가 어떻게 동작하는지 계속 살펴보죠.

예시 (Examples)

팝오버 활성화 (Enable popovers)

위에서 언급했듯이, 팝오버를 사용하려면 먼저 초기화해야 합니다. 페이지의 모든 팝오버를 초기화하는 한 가지 방법은 다음과 같이 data-bs-toggle 속성으로 선택하는 것입니다:

const popoverTriggerList = document.querySelectorAll('[data-bs-toggle="popover"]')
const popoverList = [...popoverTriggerList].map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))

라이브 데모 (Live demo)

위의 스니펫과 비슷한 JavaScript를 사용해 다음 라이브 팝오버를 렌더링합니다. 제목은 data-bs-title로, 본문 콘텐츠는 data-bs-content로 설정합니다.

HTML에서 title이나 data-bs-title 중 원하는 것을 사용해도 됩니다. title을 사용하면 Popper가 요소가 렌더링될 때 자동으로 data-bs-title로 교체합니다.

<button type="button" class="btn btn-lg btn-danger" data-bs-toggle="popover" data-bs-title="Popover title" data-bs-content="And here’s some amazing content. It’s very engaging. Right?">Click to toggle popover</button>

네 방향 (Four directions)

네 가지 옵션을 사용할 수 있습니다: top, right, bottom, left. Bootstrap을 RTL로 사용할 때는 방향이 반전됩니다. 방향을 바꾸려면 data-bs-placement를 설정하세요.

<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" data-bs-content="Top popover">
  Popover on top
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="right" data-bs-content="Right popover">
  Popover on right
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="bottom" data-bs-content="Bottom popover">
  Popover on bottom
</button>
<button type="button" class="btn btn-secondary" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="left" data-bs-content="Left popover">
  Popover on left
</button>

커스텀 container

부모 요소에 팝오버와 간섭하는 스타일이 있다면 커스텀 container를 지정해서 팝오버의 HTML이 그 요소 안에 나타나게 하고 싶을 것입니다. 반응형 테이블, 입력 그룹 등에서 흔합니다.

const popover = new bootstrap.Popover('.example-popover', {
  container: 'body'
})

명시적인 커스텀 container를 설정하고 싶은 또 다른 상황은 modal 대화상자 안에서 팝오버를 쓸 때입니다. 팝오버 자체가 modal에 추가되도록 하려는 것이죠. 상호작용 요소를 담은 팝오버에서는 특히 중요합니다 — modal 대화상자는 포커스를 가두기 때문에, 팝오버가 modal의 자식 요소가 아니라면 사용자는 이 상호작용 요소들을 포커스하거나 활성화할 수 없습니다.

const popover = new bootstrap.Popover('.example-popover', {
  container: '.modal-body'
})

커스텀 팝오버 (Custom popovers)

v5.2.0에서 추가됨

CSS 변수를 사용해 팝오버의 모양을 커스터마이즈할 수 있습니다. 커스텀 모양을 범위 지정하기 위해 data-bs-custom-class="custom-popover"으로 커스텀 클래스를 설정하고, 그 클래스로 로컬 CSS 변수 일부를 덮어쓰는 데 사용합니다.

.custom-popover {
  --bs-popover-max-width: 200px;
  --bs-popover-border-color: var(--bd-violet-bg);
  --bs-popover-header-bg: var(--bd-violet-bg);
  --bs-popover-header-color: var(--bs-white);
  --bs-popover-body-padding-x: 1rem;
  --bs-popover-body-padding-y: .5rem;
}
<button type="button" class="btn btn-secondary"
        data-bs-toggle="popover" data-bs-placement="right"
        data-bs-custom-class="custom-popover"
        data-bs-title="Custom popover"
        data-bs-content="This popover is themed via CSS variables.">
  Custom popover
</button>

다음 클릭에서 닫기 (Dismiss on next click)

사용자가 토글 요소가 아닌 다른 요소를 다음에 클릭할 때 팝오버를 닫으려면 focus 트리거를 사용하세요.

다음 클릭에서 닫는 것은 브라우저와 플랫폼에 걸쳐 올바른 동작을 위해 특정 HTML이 필요합니다. <button>이 아닌 <a> 요소만 사용할 수 있으며, tabindex를 포함해야 합니다.

<a tabindex="0" class="btn btn-lg btn-danger" role="button" data-bs-toggle="popover" data-bs-trigger="focus" data-bs-title="Dismissible popover" data-bs-content="And here’s some amazing content. It’s very engaging. Right?">Dismissible popover</a>
const popover = new bootstrap.Popover('.popover-dismiss', {
  trigger: 'focus'
})

비활성 요소 (Disabled elements)

disabled 속성이 있는 요소는 상호작용적이지 않으므로, 사용자가 호버하거나 클릭해서 팝오버(또는 툴팁)를 트리거할 수 없습니다. 해결 방법으로 래퍼 <div>나 <span>에서 팝오버를 트리거하되, 이상적으로는 tabindex="0"으로 키보드 포커스가 가능하게 만드세요.

비활성 팝오버 트리거의 경우 data-bs-trigger="hover focus"를 선호할 수도 있습니다. 사용자가 비활성 요소를 클릭할 것을 기대하지 않을 수 있으므로, 팝오버가 즉각적인 시각적 피드백으로 나타나게 하기 위해서입니다.

<span class="d-inline-block" tabindex="0" data-bs-toggle="popover" data-bs-trigger="hover focus" data-bs-content="Disabled popover">
  <button class="btn btn-primary" type="button" disabled>Disabled button</button>
</span>

CSS

변수 (Variables)

v5.2.0에서 추가됨

Bootstrap의 진화하는 CSS 변수 접근 방식의 일부로, 팝오버는 이제 향상된 실시간 커스터마이즈를 위해 .popover에 로컬 CSS 변수를 사용합니다. CSS 변수의 값은 Sass로 설정되므로 Sass 커스터마이즈도 여전히 지원됩니다.

--#{$prefix}popover-zindex: #{$zindex-popover};
--#{$prefix}popover-max-width: #{$popover-max-width};
@include rfs($popover-font-size, --#{$prefix}popover-font-size);
--#{$prefix}popover-bg: #{$popover-bg};
--#{$prefix}popover-border-width: #{$popover-border-width};
--#{$prefix}popover-border-color: #{$popover-border-color};
--#{$prefix}popover-border-radius: #{$popover-border-radius};
--#{$prefix}popover-inner-border-radius: #{$popover-inner-border-radius};
--#{$prefix}popover-box-shadow: #{$popover-box-shadow};
--#{$prefix}popover-header-padding-x: #{$popover-header-padding-x};
--#{$prefix}popover-header-padding-y: #{$popover-header-padding-y};
@include rfs($popover-header-font-size, --#{$prefix}popover-header-font-size);
--#{$prefix}popover-header-color: #{$popover-header-color};
--#{$prefix}popover-header-bg: #{$popover-header-bg};
--#{$prefix}popover-body-padding-x: #{$popover-body-padding-x};
--#{$prefix}popover-body-padding-y: #{$popover-body-padding-y};
--#{$prefix}popover-body-color: #{$popover-body-color};
--#{$prefix}popover-arrow-width: #{$popover-arrow-width};
--#{$prefix}popover-arrow-height: #{$popover-arrow-height};
--#{$prefix}popover-arrow-border: var(--#{$prefix}popover-border-color);

Sass 변수

$popover-font-size:                 $font-size-sm;
$popover-bg:                        var(--#{$prefix}body-bg);
$popover-max-width:                 276px;
$popover-border-width:              var(--#{$prefix}border-width);
$popover-border-color:              var(--#{$prefix}border-color-translucent);
$popover-border-radius:             var(--#{$prefix}border-radius-lg);
$popover-inner-border-radius:       calc(#{$popover-border-radius} - #{$popover-border-width}); // stylelint-disable-line function-disallowed-list
$popover-box-shadow:                var(--#{$prefix}box-shadow);

$popover-header-font-size:          $font-size-base;
$popover-header-bg:                 var(--#{$prefix}secondary-bg);
$popover-header-color:              $headings-color;
$popover-header-padding-y:          .5rem;
$popover-header-padding-x:          $spacer;

$popover-body-color:                var(--#{$prefix}body-color);
$popover-body-padding-y:            $spacer;
$popover-body-padding-x:            $spacer;

$popover-arrow-width:               1rem;
$popover-arrow-height:              .5rem;

사용법 (Usage)

JavaScript로 팝오버를 활성화하세요:

const exampleEl = document.getElementById('example')
const popover = new bootstrap.Popover(exampleEl, options)

팝오버는 전통적으로 키보드 포커스가 가능하고 상호작용적인 HTML 요소(링크나 폼 컨트롤 등)에만 추가해서 키보드 및 보조 기술 사용자에게 접근 가능하게 유지하세요. 다른 HTML 요소도 tabindex="0"을 추가해 포커스 가능하게 만들 수 있지만, 이렇게 하면 비상호작용 요소에 키보드 사용자에게 성가시고 혼란스러운 탭 정지(tab stop)가 생길 수 있고, 대부분의 보조 기술은 이런 상황에서 팝오버를 알려주지 않습니다. 또한 팝오버 트리거를 hover에만 의존하지 마세요. 그러면 키보드 사용자가 트리거할 수 없게 됩니다.

html 옵션으로 팝오버에 과도한 양의 콘텐츠를 추가하는 것은 피하세요. 팝오버가 표시되면 그 콘텐츠는 aria-describedby 속성으로 트리거 요소와 연결되어, 보조 기술 사용자에게 팝오버의 모든 콘텐츠가 하나의 길고 끊기지 않는 흐름으로 알려집니다.

팝오버는 키보드 포커스 순서를 관리하지 않고, DOM에서 위치가 임의적일 수 있습니다. 따라서 폼이나 링크 같은 상호작용 요소를 추가할 때는 주의하세요. 비논리적인 포커스 순서가 생기거나 키보드 사용자가 팝오버 콘텐츠에 완전히 도달하지 못할 수 있습니다. 이런 요소를 꼭 사용해야 한다면 modal 대화상자 사용을 고려하세요.

옵션 (Options)

옵션은 data 속성이나 JavaScript로 전달할 수 있으므로, data-bs-animation="{value}"처럼 data-bs-에 옵션 이름을 붙일 수 있습니다. data 속성으로 옵션을 전달할 때는 옵션 이름의 대소문자 형태를 "camelCase"에서 "kebab-case"로 바꿔야 합니다. 예를 들어 data-bs-customClass="beautifier" 대신 data-bs-custom-class="beautifier"를 사용하세요.

Bootstrap 5.2.0부터 모든 컴포넌트는 JSON 문자열로 간단한 컴포넌트 설정을 담을 수 있는 실험적인 예약 data 속성 data-bs-config를 지원합니다. 요소에 data-bs-config='{"delay":0, "title":123}'과 data-bs-title="456" 속성이 있으면 최종 title 값은 456이 되고, 별도의 data 속성이 data-bs-config에 주어진 값을 덮어씁니다. 또한 기존 data 속성도 data-bs-delay='{"show":0,"hide":150}'처럼 JSON 값을 담을 수 있습니다.

최종 설정 객체는 data-bs-config, data-bs-, js object를 병합한 결과이며, 가장 마지막에 주어진 키-값이 나머지를 덮어씁니다.

보안상의 이유로 sanitize, sanitizeFn, allowList 옵션은 data 속성으로 제공할 수 없습니다.

이름 타입 기본값 설명
allowList object 기본값 허용된 태그와 속성을 담고 있는 객체입니다. 명시적으로 허용되지 않은 것은 콘텐츠 살균기가 제거합니다. 이 목록에 추가할 때는 주의하세요. 자세한 내용은 OWASP의 Cross Site Scripting Prevention Cheat Sheet를 참고하세요.
animation boolean true 팝오버에 CSS fade 전환을 적용합니다.
boundary string, element 'clippingParents' 팝오버의 overflow 제약 경계입니다(Popper의 preventOverflow modifier에만 적용). 기본값은 'clippingParents'이며 HTMLElement 참조(JavaScript로만)를 받을 수 있습니다. 자세한 내용은 Popper의 detectOverflow 문서를 참고하세요.
container string, element, false false 팝오버를 특정 요소에 추가합니다. 예: container: 'body'. 이 옵션은 트리거 요소 근처의 문서 흐름에 팝오버를 배치할 수 있게 해 주어 특히 유용합니다. 창 크기가 조정되는 동안 팝오버가 트리거 요소에서 떠내려가는 것을 막아 줍니다.
content string, element, function '' 팝오버의 텍스트 콘텐츠입니다. 함수가 주어지면 팝오버가 붙어 있는 요소로 this 참조가 설정된 채 호출됩니다.
customClass string, function '' 팝오버가 표시될 때 클래스를 추가합니다. 이 클래스들은 template에 지정된 어떤 클래스에 더해 추가됩니다. 여러 클래스를 추가하려면 공백으로 구분하세요: 'class-1 class-2'. 추가 클래스 이름을 담은 단일 문자열을 반환하는 함수를 전달할 수도 있습니다.
delay number, object 0 팝오버 표시/숨기기를 지연합니다(ms) — 수동(manual) 트리거 타입에는 적용되지 않습니다. 숫자가 주어지면 hide/show 양쪽에 지연이 적용됩니다. 객체 구조는 delay: { "show": 500, "hide": 100 }입니다.
fallbackPlacements string, array ['top', 'right', 'bottom', 'left'] 배열에 배치 목록(우선순위 순서)을 제공해 대체 배치를 정의합니다. 자세한 내용은 Popper의 behavior 문서를 참고하세요.
html boolean false 팝오버에서 HTML을 허용합니다. true면 팝오버의 title에 있는 HTML 태그가 팝오버에서 렌더링됩니다. false면 innerText 속성이 콘텐츠를 DOM에 삽입하는 데 사용됩니다. XSS 공격을 막으려면 사용자 생성 입력을 다룰 때는 텍스트를 선호하세요.
offset number, string, function [0, 8] 팝오버의 대상에 대한 오프셋입니다. data 속성에서는 data-bs-offset="10,20"처럼 쉼표로 구분된 값을 가진 문자열을 전달할 수 있습니다. 함수로 오프셋을 결정할 때는 첫 번째 인자로 popper placement, reference, popper rects를 담은 객체와 함께 호출됩니다. 트리거 요소의 DOM 노드는 두 번째 인자로 전달됩니다. 함수는 skidding, distance의 두 숫자를 담은 배열을 반환해야 합니다. 자세한 내용은 Popper의 offset 문서를 참고하세요.
placement string, function 'right' 팝오버 위치 지정 방법: auto, top, bottom, left, right. auto를 지정하면 팝오버를 동적으로 방향을 바꿉니다. 함수로 배치를 결정할 때는 첫 번째 인자로 팝오버 DOM 노드, 두 번째 인자로 트리거 요소 DOM 노드와 함께 호출됩니다. this 컨텍스트는 팝오버 인스턴스로 설정됩니다.
popperConfig null, object, function null Bootstrap의 기본 Popper 설정을 바꾸려면 Popper의 configuration을 참고하세요. Popper 설정을 만드는 데 함수를 사용하면 Bootstrap의 기본 Popper 설정을 담은 객체와 함께 호출됩니다. 기본값과 자신의 설정을 병합하고 사용하는 데 도움이 됩니다. 함수는 Popper용 설정 객체를 반환해야 합니다.
sanitize boolean true 콘텐츠 살균을 활성화합니다. true면 template, content, title 옵션이 살균됩니다. 콘텐츠 살균을 비활성화할 때는 주의하세요. 자세한 내용은 OWASP의 Cross Site Scripting Prevention Cheat Sheet를 참고하세요. 콘텐츠 살균 비활성화만으로 인한 취약점은 Bootstrap의 보안 모델 범위에 포함되지 않습니다.
sanitizeFn null, function null 대체 콘텐츠 살균 함수를 제공합니다. 살균을 수행하기 위해 전용 라이브러리를 선호한다면 유용합니다.
selector string, false false selector가 제공되면 팝오버 객체가 지정된 대상에 위임됩니다. 실제로는 동적으로 추가된 DOM 요소에도 팝오버를 적용하는 데 사용됩니다(jQuery.on 지원). 이 이슈와 유익한 예시를 참고하세요. 참고: title 속성은 selector로 사용하면 안 됩니다.
template string '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>' 팝오버를 만들 때 사용할 기본 HTML입니다. 팝오버의 title은 .popover-header에 주입됩니다. 팝오버의 content는 .popover-body에 주입됩니다. .popover-arrow는 팝오버의 화살표가 됩니다. 가장 바깥 래퍼 요소에는 .popover 클래스와 role="tooltip"이 있어야 합니다.
title string, element, function '' 팝오버 제목입니다. 함수가 주어지면 팝오버가 붙어 있는 요소로 this 참조가 설정된 채 호출됩니다.
trigger string 'click' 팝오버 트리거 방법: click, hover, focus, manual. 여러 트리거를 공백으로 구분해 전달할 수 있습니다. 'manual'은 팝오버가 .popover('show'), .popover('hide'), .popover('toggle') 메서드를 통해 프로그램 방식으로 트리거됨을 나타냅니다. 이 값은 다른 어떤 트리거와도 결합할 수 없습니다. 'hover' 단독은 키보드로 트리거할 수 없는 팝오버를 만들며, 키보드 사용자에게 동일한 정보를 전달하는 대체 방법이 있을 때만 사용해야 합니다.

개별 팝오버의 data 속성 (Data attributes for individual popovers)

개별 팝오버의 옵션은 위에서 설명한 대로 data 속성을 사용해 대체로 지정할 수 있습니다.

popperConfig와 함께 함수 사용 (Using function with popperConfig)

const popover = new bootstrap.Popover(element, {
  popperConfig(defaultBsPopperConfig) {
    // const newPopperConfig = {...}
    // use defaultBsPopperConfig if needed...
    // return newPopperConfig
  }
})

메서드 (Methods)

모든 API 메서드는 비동기이며 전환을 시작합니다. 전환이 시작되는 즉시 호출자에게 반환되지만, 끝나기 전에 반환됩니다. 또한 전환 중인 컴포넌트에 대한 메서드 호출은 무시됩니다. JavaScript 문서에서 자세히 알아보세요.

메서드 설명
disable 요소의 팝오버가 표시되는 능력을 제거합니다. 다시 활성화되어야만 팝오버가 표시될 수 있습니다.
dispose 요소의 팝오버를 숨기고 파괴합니다(DOM 요소에 저장된 데이터를 제거합니다). 위임(delegation)을 사용하는 팝오버(selector 옵션으로 생성)는 하위 트리거 요소에서 개별적으로 파괴할 수 없습니다.
enable 요소의 팝오버에 표시될 수 있는 능력을 부여합니다. 팝오버는 기본적으로 활성화되어 있습니다.
getInstance DOM 요소와 연결된 팝오버 인스턴스를 가져올 수 있게 해 주는 정적 메서드입니다.
getOrCreateInstance DOM 요소와 연결된 팝오버 인스턴스를 가져오거나, 초기화되지 않은 경우 새로 만드는 정적 메서드입니다.
hide 요소의 팝오버를 숨깁니다. 팝오버가 실제로 숨겨지기 전에(hidden.bs.popover 이벤트 전에) 호출자에게 반환합니다. 팝오버의 "수동(manual)" 트리거로 간주됩니다.
setContent 초기화 후 팝오버의 콘텐츠를 바꿀 수 있는 방법을 제공합니다.
show 요소의 팝오버를 표시합니다. 팝오버가 실제로 표시되기 전에(shown.bs.popover 이벤트 전에) 호출자에게 반환합니다. 팝오버의 "수동(manual)" 트리거로 간주됩니다. 제목과 콘텐츠가 모두 제로 길이인 팝오버는 절대 표시되지 않습니다.
toggle 요소의 팝오버를 토글합니다. 팝오버가 실제로 표시되거나 숨겨지기 전에(shown.bs.popover 또는 hidden.bs.popover 이벤트 전에) 호출자에게 반환합니다. 팝오버의 "수동(manual)" 트리거로 간주됩니다.
toggleEnabled 요소의 팝오버가 표시되거나 숨겨질 수 있는 능력을 토글합니다.
update 요소의 팝오버 위치를 업데이트합니다.
// getOrCreateInstance example
const popover = bootstrap.Popover.getOrCreateInstance('#example') // Returns a Bootstrap popover instance

// setContent example
popover.setContent({
  '.popover-header': 'another title',
  '.popover-body': 'another content'
})

setContent 메서드는 object 인자를 받는데, 각 속성-키는 팝오버 template 안의 유효한 string selector이고, 각 관련 속성-값은 string | element | function | null이 될 수 있습니다.

이벤트 (Events)

이벤트 설명
hide.bs.popover hide 인스턴스 메서드가 호출될 때 즉시 발생하는 이벤트입니다.
hidden.bs.popover 팝오버가 사용자에게 숨겨지는 것을 마쳤을 때 발생하는 이벤트입니다(CSS 전환이 완료될 때까지 기다립니다).
inserted.bs.popover 팝오버 template이 DOM에 추가된 후 show.bs.popover 이벤트 다음에 발생하는 이벤트입니다.
show.bs.popover show 인스턴스 메서드가 호출될 때 즉시 발생하는 이벤트입니다.
shown.bs.popover 팝오버가 사용자에게 표시되었을 때 발생하는 이벤트입니다(CSS 전환이 완료될 때까지 기다립니다).
const myPopoverTrigger = document.getElementById('myPopover')
myPopoverTrigger.addEventListener('hidden.bs.popover', () => {
  // do something...
})

더 알아보기 (Learn more)