@@ -64,8 +64,10 @@ const OffsetMethod = {
64
64
POSITION : 'position'
65
65
}
66
66
67
- // HREFs must start with # but can be === '#', or start with '#/' or '#!' (which can be router links)
68
- const HREF_REGEX = / ^ # [ ^ / ! ] + /
67
+ // HREFs must end with a hash followed by at least one non-hash character.
68
+ // HREFs in the links are assumed to point to non-external links.
69
+ // Comparison to the current page base URL is not performed!
70
+ const HREF_REGEX = / ^ .* ( # [ ^ # ] + ) $ /
69
71
70
72
// Transition Events
71
73
const TransitionEndEvents = [
@@ -105,6 +107,7 @@ function typeCheckConfig(
105
107
valueType = value && value . _isVue ? 'component' : valueType
106
108
107
109
if ( ! new RegExp ( expectedTypes ) . test ( valueType ) ) {
110
+ /* istanbul ignore next */
108
111
warn (
109
112
`${ componentName } : Option "${ property } " provided type "${ valueType } " but expected type "${ expectedTypes } "`
110
113
)
@@ -301,24 +304,34 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ {
301
304
302
305
this . $scrollHeight = this . getScrollHeight ( )
303
306
304
- // Find all the unique link href's
307
+ // Find all the unique link href's that we will control
305
308
selectAll ( this . $selector , this . $el )
309
+ // Get HREF value
306
310
. map ( link => getAttr ( link , 'href' ) )
307
- . filter ( href => HREF_REGEX . test ( href || '' ) )
311
+ // Filter out HREFs taht do not match our RegExp
312
+ . filter ( href => href && HREF_REGEX . test ( href || '' ) )
313
+ // Find all elements with ID that match HREF hash
308
314
. map ( href => {
309
- const el = select ( href , scroller )
310
- if ( isVisible ( el ) ) {
315
+ // Convert HREF into an ID (including # at begining)
316
+ const id = href . replace ( HREF_REGEX , '$1' ) . trim ( )
317
+ if ( ! id ) {
318
+ return null
319
+ }
320
+ // Find the element with the ID specified by id
321
+ const el = select ( id , scroller )
322
+ if ( el && isVisible ( el ) ) {
311
323
return {
312
324
offset : parseInt ( methodFn ( el ) . top , 10 ) + offsetBase ,
313
- target : href
325
+ target : id
314
326
}
315
327
}
316
328
return null
317
329
} )
318
- . filter ( item => item )
330
+ . filter ( Boolean )
331
+ // Sort them by their offsets (smallest first)
319
332
. sort ( ( a , b ) => a . offset - b . offset )
333
+ // record only unique targets/offsets
320
334
. reduce ( ( memo , item ) => {
321
- // record only unique targets/offfsets
322
335
if ( ! memo [ item . target ] ) {
323
336
this . $offsets . push ( item . offset )
324
337
this . $targets . push ( item . target )
@@ -327,6 +340,7 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ {
327
340
return memo
328
341
} , { } )
329
342
343
+ // Return this for easy chaining
330
344
return this
331
345
}
332
346
@@ -409,8 +423,11 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ {
409
423
// Grab the list of target links (<a href="{$target}">)
410
424
const links = selectAll (
411
425
this . $selector
426
+ // Split out the base selectors
412
427
. split ( ',' )
413
- . map ( selector => `${ selector } [href="${ target } "]` )
428
+ // Map to a selector that matches links with HREF ending in the ID (including '#')
429
+ . map ( selector => `${ selector } [href$="${ target } "]` )
430
+ // Join back into a single selector string
414
431
. join ( ',' ) ,
415
432
this . $el
416
433
)
@@ -437,11 +454,11 @@ class ScrollSpy /* istanbul ignore next: not easy to test */ {
437
454
while ( el ) {
438
455
el = closest ( Selector . NAV_LIST_GROUP , el )
439
456
const sibling = el ? el . previousElementSibling : null
440
- if ( matches ( sibling , `${ Selector . NAV_LINKS } , ${ Selector . LIST_ITEMS } ` ) ) {
457
+ if ( sibling && matches ( sibling , `${ Selector . NAV_LINKS } , ${ Selector . LIST_ITEMS } ` ) ) {
441
458
this . setActiveState ( sibling , true )
442
459
}
443
460
// Handle special case where nav-link is inside a nav-item
444
- if ( matches ( sibling , Selector . NAV_ITEMS ) ) {
461
+ if ( sibling && matches ( sibling , Selector . NAV_ITEMS ) ) {
445
462
this . setActiveState ( select ( Selector . NAV_LINKS , sibling ) , true )
446
463
// Add active state to nav-item as well
447
464
this . setActiveState ( sibling , true )
0 commit comments