}
static dataApiKeydownHandler(event) {
- const isInput = /input|textarea/i.test(event.target.tagName)
+ // Treat contenteditable hosts (e.g. rich-text editors) like inputs so the
+ // menu doesn't hijack their arrow keys.
+ const isInput = /input|textarea/i.test(event.target.tagName) || event.target.isContentEditable
const isEscapeEvent = event.key === ESCAPE_KEY
const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)
const isLeftOrRightEvent = [ARROW_LEFT_KEY, ARROW_RIGHT_KEY].includes(event.key)
})
})
+ it('should ignore keyboard events within contenteditable elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div>',
+ ' <button class="btn" data-bs-toggle="menu">Menu</button>',
+ ' <div class="menu">',
+ ' <a class="menu-item" href="#sub1">Submenu 1</a>',
+ ' <div class="editor" contenteditable="true"></div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const triggerMenu = fixtureEl.querySelector('[data-bs-toggle="menu"]')
+ const editor = fixtureEl.querySelector('.editor')
+
+ triggerMenu.addEventListener('shown.bs.menu', () => {
+ editor.focus()
+ const keydown = createEvent('keydown')
+
+ keydown.key = 'ArrowUp'
+ editor.dispatchEvent(keydown)
+
+ expect(document.activeElement).toEqual(editor, 'contenteditable still focused')
+ resolve()
+ })
+
+ triggerMenu.click()
+ })
+ })
+
it('should skip disabled element when using keyboard navigation', () => {
return new Promise(resolve => {
fixtureEl.innerHTML = [
top: var(--popover-border-width);
border-block-end-color: var(--popover-bg);
}
+
+ // When the popover has a header, the bottom arrow points into the header,
+ // so its fill should match the header background, not the body background.
+ &:has(+ .popover-header)::after {
+ border-block-end-color: var(--popover-header-bg);
+ }
}
// This will remove the popover-header's border just below the arrow
| `animation` | boolean | `true` | Apply a CSS fade transition to the popover. |
| `boundary` | string, element | `'clippingParents'` | Overflow constraint boundary of the popover (applies only to Floating UI’s shift middleware). By default, it’s `'clippingParents'` and can accept an HTMLElement reference (via JavaScript only). For more information refer to Floating UI’s [shift docs](https://floating-ui.com/docs/shift#boundary). |
| `container` | string, element, false | `false` | Appends the popover to a specific element. Example: `container: 'body'`. This option is particularly useful in that it allows you to position the popover in the flow of the document near the triggering element - which will prevent the popover from floating away from the triggering element during a window resize. |
-| `content` | string, element, function | `''` | The popover’s text content. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
+| `content` | string, element, function | `''` | The popover’s text content. If a function is given, it will be called with the element it is attached to as its first argument, and with its `this` reference set to that same element. |
| `customClass` | string, function | `''` | Add classes to the popover when it is shown. Note that these classes will be added in addition to any classes specified in the template. To add multiple classes, separate them with spaces: `'class-1 class-2'`. You can also pass a function that should return a single string containing additional class names. |
| `delay` | number, object | `0` | Delay showing and hiding the popover (ms)—doesn’t apply to manual trigger type. If a number is supplied, delay is applied to both hide/show. Object structure is: `delay: { "show": 500, "hide": 100 }`. |
| `fallbackPlacements` | array | `['top', 'right', 'bottom', 'left']` | Define fallback placements by providing a list of placements in array (in order of preference). For more information refer to Floating UI’s [flip docs](https://floating-ui.com/docs/flip#fallbackplacements). |
| `sanitizeFn` | null, function | `null` | Provide an alternative [content sanitization]([[docsref:/getting-started/javascript#sanitizer]]) function. This can be useful if you prefer to use a dedicated library to perform sanitization. |
| `selector` | string, false | `false` | If a selector is provided, popover objects will be delegated to the specified targets. In practice, this is used to also apply popovers to dynamically added DOM elements (`jQuery.on` support). See [this issue]([[config:repo]]/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). **Note**: `title` attribute must not be used as a selector. |
| `template` | string | `'<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'` | Base HTML to use when creating the popover. The popover’s `title` will be injected into the `.popover-header`. The popover’s `content` will be injected into the `.popover-body`. `.popover-arrow` will become the popover’s arrow. The outermost wrapper element should have the `.popover` class and `role="tooltip"`. |
-| `title` | string, element, function | `''` | The popover title. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
+| `title` | string, element, function | `''` | The popover title. If a function is given, it will be called with the element it is attached to as its first argument, and with its `this` reference set to that same element. |
| `trigger` | string | `'click'` | How popover is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the popover will be triggered programmatically via the `.popover('show')`, `.popover('hide')` and `.popover('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in popovers that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. |
</BsTable>
| `sanitizeFn` | null, function | `null` | Provide an alternative [content sanitization]([[docsref:/getting-started/javascript#sanitizer]]) function. This can be useful if you prefer to use a dedicated library to perform sanitization. |
| `selector` | string, false | `false` | If a selector is provided, tooltip objects will be delegated to the specified targets. In practice, this is used to also apply tooltips to dynamically added DOM elements (`jQuery.on` support). See [this issue]([[config:repo]]/issues/4215) and [an informative example](https://codepen.io/Johann-S/pen/djJYPb). **Note**: `title` attribute must not be used as a selector. |
| `template` | string | `'<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>'` | Base HTML to use when creating the tooltip. The tooltip’s `title` will be injected into the `.tooltip-inner`. `.tooltip-arrow` will become the tooltip’s arrow. The outermost wrapper element should have the `.tooltip` class and `role="tooltip"`. |
-| `title` | string, element, function | `''` | The tooltip title. If a function is given, it will be called with its `this` reference set to the element that the popover is attached to. |
+| `title` | string, element, function | `''` | The tooltip title. If a function is given, it will be called with the element it is attached to as its first argument, and with its `this` reference set to that same element. |
| `trigger` | string | `'hover focus'` | How tooltip is triggered: click, hover, focus, manual. You may pass multiple triggers; separate them with a space. `'manual'` indicates that the tooltip will be triggered programmatically via the `.tooltip('show')`, `.tooltip('hide')` and `.tooltip('toggle')` methods; this value cannot be combined with any other trigger. `'hover'` on its own will result in tooltips that cannot be triggered via the keyboard, and should only be used if alternative methods for conveying the same information for keyboard users is present. |
</BsTable>