@@ -8,7 +8,7 @@ function Highlighter(options = hhDefaultOptions) {
88 options [ key ] = options [ key ] ?? hhDefaultOptions [ key ] ;
99 }
1010
11- this . annotatableContainer , this . relativeAncestorElement , this . annotatableParagraphs ;
11+ this . annotatableContainer , this . relativeAncestorElement , this . annotatableParagraphs , this . stylesheets ;
1212 let generalStylesheet , appearanceStylesheet , highlightApiStylesheet , selectionStylesheet ;
1313 let annotatableParagraphIds , hyperlinkElements ;
1414 let svgBackground , svgActiveOverlay , selectionHandles ;
@@ -50,14 +50,11 @@ function Highlighter(options = hhDefaultOptions) {
5050 document . body . tabIndex = - 1 ;
5151
5252 // Set up stylesheets
53- generalStylesheet = new CSSStyleSheet ( ) ;
54- appearanceStylesheet = new CSSStyleSheet ( ) ;
55- highlightApiStylesheet = new CSSStyleSheet ( ) ;
56- selectionStylesheet = new CSSStyleSheet ( ) ;
57- document . adoptedStyleSheets . push ( generalStylesheet ) ;
58- document . adoptedStyleSheets . push ( appearanceStylesheet ) ;
59- document . adoptedStyleSheets . push ( highlightApiStylesheet ) ;
60- document . adoptedStyleSheets . push ( selectionStylesheet ) ;
53+ this . stylesheets = { }
54+ generalStylesheet = createStylesheet ( this . stylesheets , 'general' ) ;
55+ appearanceStylesheet = createStylesheet ( this . stylesheets , 'appearance' ) ;
56+ highlightApiStylesheet = createStylesheet ( this . stylesheets , 'highlight-api' ) ;
57+ selectionStylesheet = createStylesheet ( this . stylesheets , 'selection' ) ;
6158 generalStylesheet . replaceSync ( `
6259 ${ options . containerSelector } {
6360 -webkit-tap-highlight-color: transparent;
@@ -559,11 +556,7 @@ function Highlighter(options = hhDefaultOptions) {
559556
560557 // Remove this Highlighter instance and its highlights
561558 this . removeHighlighter = ( ) => {
562- generalStylesheet . replaceSync ( '' ) ;
563- appearanceStylesheet . replaceSync ( '' ) ;
564- highlightApiStylesheet . replaceSync ( '' ) ;
565- selectionStylesheet . replaceSync ( '' ) ;
566-
559+ for ( const stylesheet of Object . values ( this . stylesheets ) ) if ( stylesheet . parentElement ) stylesheet . remove ( ) ;
567560 this . loadHighlights ( [ ] ) ;
568561 this . annotatableContainer . querySelectorAll ( '.hh-svg-background, .hh-selection-handle' ) . forEach ( el => el . remove ( ) )
569562 controller . abort ( ) ;
@@ -1055,6 +1048,31 @@ function Highlighter(options = hhDefaultOptions) {
10551048 return styleTemplate ;
10561049 }
10571050
1051+ // Create a CSS stylesheet
1052+ function createStylesheet ( stylesheets , stylesheetKey ) {
1053+ let stylesheet = stylesheets [ stylesheetKey ] ;
1054+ if ( ! stylesheet ) {
1055+ if ( supportsCssStylesheetApi ) {
1056+ stylesheet = new CSSStyleSheet ( ) ;
1057+ document . adoptedStyleSheets . push ( stylesheet ) ;
1058+ } else {
1059+ // For browsers that don't fully support the CSSStyleSheet API, such as Safari < 16.4.
1060+ // See https://developer.mozilla.org/en-US/docs/Web/API/CSSStyleSheet#browser_compatibility
1061+ stylesheet = document . createElement ( 'style' ) ;
1062+ stylesheet . appendChild ( document . createTextNode ( '' ) ) ;
1063+ stylesheet . replaceSync = ( newContent ) => {
1064+ stylesheet . textContent = newContent ;
1065+ }
1066+ stylesheet . insertRule = ( newContent ) => {
1067+ stylesheet . textContent += newContent ;
1068+ }
1069+ document . head . appendChild ( stylesheet ) ;
1070+ }
1071+ stylesheets [ stylesheetKey ] = stylesheet ;
1072+ }
1073+ return stylesheet ;
1074+ }
1075+
10581076 // Restore the previous selection range in case the browser clears the selection
10591077 const getRestoredSelectionOrCaret = ( selection , pointerEvent = null ) => {
10601078 if ( selection . type === 'None' ) {
@@ -1091,14 +1109,12 @@ function Highlighter(options = hhDefaultOptions) {
10911109 // Adapted from https://stackoverflow.com/a/12924488/1349044
10921110 const getCaretFromCoordinates = ( clientX , clientY , checkAnnotatable = false , checkXDistance = false , checkYDistance = false ) => {
10931111 let range ;
1094- if ( document . caretPositionFromPoint ) {
1095- // Most browsers
1112+ if ( supportsCaretPositionFromPoint ) {
10961113 let caretPosition = document . caretPositionFromPoint ( clientX , clientY ) ;
10971114 range = document . createRange ( ) ;
10981115 range . setStart ( caretPosition . offsetNode , caretPosition . offset ) ;
10991116 range . collapse ( true ) ;
1100- } else if ( document . caretRangeFromPoint ) {
1101- // Safari
1117+ } else if ( supportsCaretRangeFromPoint ) {
11021118 range = document . caretRangeFromPoint ( clientX , clientY ) ;
11031119 }
11041120 if ( ! range ) return ;
@@ -1227,6 +1243,9 @@ let hhHighlighters = [];
12271243const isTouchDevice = navigator . maxTouchPoints > 0 ;
12281244const isWebKit = / ^ ( (? ! C h r o m e | F i r e f o x | A n d r o i d | S a m s u n g ) .) * A p p l e W e b K i t / i. test ( navigator . userAgent ) ;
12291245const isWKWebView = isWebKit && window . webkit ?. messageHandlers ;
1246+ const supportsCaretPositionFromPoint = document . caretPositionFromPoint ;
1247+ const supportsCaretRangeFromPoint = document . caretRangeFromPoint ;
1248+ const supportsCssStylesheetApi = CSSStyleSheet ?. prototype ?. replaceSync ;
12301249const supportsHighlightApi = CSS . highlights ;
12311250
12321251// Workaround to allow programmatic text selection on tap in iOS Safari
0 commit comments