@@ -59,7 +59,11 @@ function saveScore(wpm, accuracy) {
5959 username : generateAnonymousUsername ( )
6060 } ;
6161 userScores . push ( score ) ;
62- if ( userScores . length > 100 ) userScores = userScores . slice ( - 100 ) ;
62+
63+ if ( userScores . length > 100 ) {
64+ userScores = userScores . slice ( - 100 ) ;
65+ }
66+
6367 localStorage . setItem ( 'typingTestScores' , JSON . stringify ( userScores ) ) ;
6468 return score ;
6569}
@@ -91,6 +95,7 @@ function displayLeaderboard(timeframe = 'daily') {
9195 leaderboardLoading . style . display = 'block' ;
9296 leaderboardEmpty . style . display = 'none' ;
9397 leaderboardList . innerHTML = '' ;
98+
9499 setTimeout ( ( ) => {
95100 const scores = getScoresByTimeframe ( timeframe )
96101 . sort ( ( a , b ) => b . wpm - a . wpm || b . accuracy - a . accuracy )
@@ -166,6 +171,7 @@ function appendWords(count = 10) {
166171 } , 10 ) ;
167172}
168173
174+ // FIXED: Update caret position
169175function updateCaret ( ) {
170176 const spans = textEl . querySelectorAll ( "span" ) ;
171177
@@ -178,23 +184,27 @@ function updateCaret() {
178184 }
179185
180186 const span = spans [ index ] ;
181-
182- // Use requestAnimationFrame for better performance and timing
183- requestAnimationFrame ( ( ) => {
184- // Get the position relative to the text container
185- const textRect = textEl . getBoundingClientRect ( ) ;
186- const spanRect = span . getBoundingClientRect ( ) ;
187-
188- // Position the caret at the beginning of the current character
189- caret . style . left = `${ spanRect . left - textRect . left } px` ;
190- caret . style . top = `${ spanRect . top - textRect . top } px` ;
191- caret . style . height = `${ spanRect . height } px` ;
192- caret . style . display = "block" ;
193- caret . style . opacity = "1" ;
194-
195- // mark current span for styling
196- span . classList . add ( "current" ) ;
197- } ) ;
187+ const textStyles = window . getComputedStyle ( textEl ) ;
188+ const paddingLeft = parseFloat ( textStyles . paddingLeft ) ;
189+ const paddingTop = parseFloat ( textStyles . paddingTop ) ;
190+
191+ // Calculate position relative to the text element's content area
192+ let left = paddingLeft ;
193+ let top = paddingTop ;
194+
195+ for ( let i = 0 ; i < index ; i ++ ) {
196+ const s = spans [ i ] ;
197+ if ( s . offsetTop === span . offsetTop ) {
198+ left += s . offsetWidth ;
199+ }
200+ }
201+
202+ top = span . offsetTop - spans [ 0 ] . offsetTop + paddingTop ;
203+
204+ caret . style . left = `${ left } px` ;
205+ caret . style . top = `${ top } px` ;
206+ caret . style . height = `${ span . offsetHeight } px` ;
207+ caret . style . display = "block" ;
198208}
199209
200210function startTimer ( ) {
@@ -212,9 +222,11 @@ function startTimer() {
212222function endTest ( ) {
213223 document . removeEventListener ( "keydown" , handleTyping ) ;
214224 caret . style . display = "none" ;
225+
215226 const minutes = ( Date . now ( ) - start ) / 60000 ;
216227 const wpm = Math . round ( ( index / 5 ) / Math . max ( minutes , 0.001 ) ) ;
217228 const acc = Math . round ( ( correct / Math . max ( index , 1 ) ) * 100 ) ;
229+
218230 const savedScore = saveScore ( wpm , acc ) ;
219231 const highScore = isHighScore ( wpm , currentTimeframe ) ;
220232 showCurrentScore ( wpm , acc , highScore ) ;
@@ -236,12 +248,10 @@ function resetTest() {
236248 textArray = [ ] ;
237249 textEl . innerHTML = "" ;
238250 appendWords ( 30 ) ;
239-
240- // Force caret to be visible and positioned
241- forceCaretVisible ( ) ;
242- setTimeout ( forceCaretVisible , 10 ) ;
243- setTimeout ( forceCaretVisible , 50 ) ;
244- setTimeout ( forceCaretVisible , 100 ) ;
251+
252+ setTimeout ( ( ) => {
253+ updateCaret ( ) ;
254+ } , 10 ) ;
245255
246256 typedText = "" ;
247257 updateTitle ( ) ; // Reset title
@@ -301,6 +311,7 @@ function handleTyping(e) {
301311
302312function initLeaderboard ( ) {
303313 displayLeaderboard ( currentTimeframe ) ;
314+
304315 timeframeBtns . forEach ( btn => {
305316 btn . addEventListener ( 'click' , ( ) => {
306317 timeframeBtns . forEach ( b => b . classList . remove ( 'active' ) ) ;
@@ -318,38 +329,12 @@ function checkScrollable() {
318329}
319330
320331window . addEventListener ( 'load' , checkScrollable ) ;
321- window . addEventListener ( 'resize' , checkScrollable ) ;
322- setTimeout ( checkScrollable , 1000 ) ;
323-
324- // Force caret to be visible
325- function forceCaretVisible ( ) {
326- if ( caret ) {
327- caret . style . display = "block" ;
328- caret . style . opacity = "1" ;
329- caret . style . visibility = "visible" ;
330- updateCaret ( ) ;
331- }
332- }
332+ window . addEventListener ( 'resize' , ( ) => {
333+ checkScrollable ( ) ;
334+ updateCaret ( ) ;
335+ } ) ;
333336
334- // Initialize
335337resetTest ( ) ;
336338initLeaderboard ( ) ;
337339restartBtn . addEventListener ( "click" , resetTest ) ;
338340timeSelect . addEventListener ( "change" , resetTest ) ;
339- window . addEventListener ( "resize" , updateCaret ) ;
340-
341- // Ensure caret is visible when page loads
342- document . addEventListener ( "DOMContentLoaded" , ( ) => {
343- setTimeout ( forceCaretVisible , 100 ) ;
344- setTimeout ( forceCaretVisible , 300 ) ;
345- setTimeout ( forceCaretVisible , 500 ) ;
346- } ) ;
347-
348- // Also ensure caret is visible after a short delay
349- setTimeout ( forceCaretVisible , 200 ) ;
350- setTimeout ( forceCaretVisible , 400 ) ;
351- setTimeout ( forceCaretVisible , 800 ) ;
352-
353- // Ensure caret is visible when window gets focus
354- window . addEventListener ( "focus" , forceCaretVisible ) ;
355- window . addEventListener ( "load" , forceCaretVisible ) ;
0 commit comments