mirror of
https://github.com/twbs/bootstrap.git
synced 2025-10-03 00:03:44 -04:00
Compare commits
4 Commits
32ab9f68a7
...
eb8bc74c40
Author | SHA1 | Date | |
---|---|---|---|
|
eb8bc74c40 | ||
|
e0d1dad9ca | ||
|
588cf7e5fc | ||
|
f84bf47e1a |
@ -9,7 +9,7 @@ import BaseComponent from './base-component.js'
|
||||
import EventHandler from './dom/event-handler.js'
|
||||
import SelectorEngine from './dom/selector-engine.js'
|
||||
import {
|
||||
defineJQueryPlugin, getElement, isDisabled, isVisible
|
||||
defineJQueryPlugin, getElement, isDisabled, isVisible, parseSelector
|
||||
} from './util/index.js'
|
||||
|
||||
/**
|
||||
@ -210,11 +210,13 @@ class ScrollSpy extends BaseComponent {
|
||||
continue
|
||||
}
|
||||
|
||||
const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)
|
||||
const withDecodeUri = decodeURI(anchor.hash)
|
||||
const withEscape = parseSelector(withDecodeUri)
|
||||
const observableSection = SelectorEngine.findOne(withEscape, this._element)
|
||||
|
||||
// ensure that the observableSection exists & is visible
|
||||
if (isVisible(observableSection)) {
|
||||
this._targetLinks.set(decodeURI(anchor.hash), anchor)
|
||||
this._targetLinks.set(withDecodeUri, anchor)
|
||||
this._observableSections.set(anchor.hash, observableSection)
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ const TRANSITION_END = 'transitionend'
|
||||
const parseSelector = selector => {
|
||||
if (selector && window.CSS && window.CSS.escape) {
|
||||
// document.querySelector needs escaping to handle IDs (html5+) containing for instance /
|
||||
selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`)
|
||||
selector = selector.replace(/#([^\s"']+)/g, (match, id) => `#${CSS.escape(id)}`)
|
||||
}
|
||||
|
||||
return selector
|
||||
|
@ -194,6 +194,34 @@ describe('ScrollSpy', () => {
|
||||
expect(scrollSpy._targetLinks.size).toBe(1)
|
||||
})
|
||||
|
||||
it('should take account of escaped IDs', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<nav id="navigation" class="navbar">',
|
||||
' <ul class="navbar-nav">',
|
||||
' <li class="nav-item"><a class="nav-link active" id="one-link" href="#div-2.1">One</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="two-link" href="#!@#$_^&*()">Two</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="three-link" href="#id.div.Element@data-custom=true">Three</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="four-link" href="#https://domain.to/#%2F%40user%3Aname.test">Four</a></li>',
|
||||
' <li class="nav-item"><a class="nav-link" id="five-link" href="#https://domain.to/#/@user:name.test">Five</a></li>',
|
||||
' </ul>',
|
||||
'</nav>',
|
||||
'<div id="content" style="height: 200px; overflow-y: auto;">',
|
||||
' <div id="div-2.1" style="height: 300px;">test</div>',
|
||||
' <div id="!@#$_^&*()" style="height: 300px;">test</div>',
|
||||
' <div id="id.div.Element@data-custom=true" style="height: 300px;">test</div>',
|
||||
' <div id="https://domain.to/#%2F%40user%3Aname.test" style="height: 300px;">test</div>',
|
||||
' <div id="https://domain.to/#/@user:name.test">test</div>',
|
||||
'</div>'
|
||||
].join('')
|
||||
|
||||
const scrollSpy = new ScrollSpy(fixtureEl.querySelector('#content'), {
|
||||
target: '#navigation'
|
||||
})
|
||||
|
||||
expect(scrollSpy._observableSections.size).toBe(5)
|
||||
expect(scrollSpy._targetLinks.size).toBe(5)
|
||||
})
|
||||
|
||||
it('should not process element without target', () => {
|
||||
fixtureEl.innerHTML = [
|
||||
'<nav id="navigation" class="navbar">',
|
||||
|
Loading…
x
Reference in New Issue
Block a user