Last active
April 26, 2021 10:32
-
-
Save wojtekmaj/b9a4144592f59567380de81d154553b1 to your computer and use it in GitHub Desktop.
Shit I need to put up with to support Safari
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const invalidOnChangeKey = '__RECENT_SELECT_NAME__'; | |
/** | |
* Apparently iOS Safari dispatches an extra onChange when the user jumps between inputs on the | |
* input the user jumped to, causing the app to select nth value in BOTH inputs! So to cope with | |
* that, we're creating a temporary variable that prevents other select elements from being | |
* changed within 100ms from the original change. | |
*/ | |
function checkOnChangeValidity(event) { | |
return !event || !window[invalidOnChangeKey] || window[invalidOnChangeKey] === event.target.name; | |
} | |
function temporarilySetFlag(event) { | |
window[invalidOnChangeKey] = event.target; | |
setTimeout(() => { | |
delete window[invalidOnChangeKey]; | |
}, 100); | |
} | |
function onChange(event) { | |
// … | |
temporarilySetFlag(event); | |
// … | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
function focusWithAnimatedScroll(element) { | |
const currentScrollY = window.scrollY; | |
const elementTopEdge = element.offsetTop - element.parentElement.offsetTop; | |
element.focus({ preventScroll: true }); | |
// Setting focus current step scrolls the page on browsers not supporting preventScroll: true | |
// (*ahem* Safari), so we scroll back down to saved value… | |
window.scrollTo({ top: currentScrollY }); | |
setImmediate(() => { | |
// Because Safari tends to delay focus for some reason, we correct the scroll position | |
// once again and only then we start animation | |
window.scrollTo({ top: currentScrollY }); | |
// …and from there, we animate scroll to top. | |
window.scrollTo({ | |
top: elementTopEdge, | |
behavior: reduceMotion ? 'auto' : 'smooth', | |
}); | |
}); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Apparently iOS Safari cannot reliably trigger onChange event on <select> elements. So to | |
* cope with that, from the moment the input is focused up until the moment it's blurred, we're | |
* monitoring its value, and if it changed but store value did not, we're manually triggering | |
* onChange event. | |
* | |
* Read more: https://stackoverflow.com/questions/8004227/ios-select-onchange-not-firing | |
*/ | |
const checkForMissedOnChange = useCallback(() => { | |
if (!select.current) { | |
return; | |
} | |
const nextValue = select.current.value; | |
if (!nextValue && !value) { | |
return; | |
} | |
const originalValue = findOriginalValue(options, nextValue); | |
if (originalValue !== value) { | |
// eslint-disable-next-line no-console | |
console.warn('Browser failed to dispatch onChange event. Dispatching onChange manually.'); | |
// Dispatch onChange manually | |
const changeEvent = createEvent('change'); | |
select.current.dispatchEvent(changeEvent); | |
} | |
}, [options, select, value]); | |
useEffect(() => { | |
if (!isFocused) { | |
return; | |
} | |
const interval = setInterval(checkForMissedOnChange, 100); | |
return () => { | |
clearInterval(interval); | |
}; | |
}, [checkForMissedOnChange, isFocused]); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment