Search code examples
javascriptreactjsreact-hooksref

My dropdown doesn't close when I click outside of it on previous iOS


I'm working on a dropdown and want it to close when I click outside of it. What I've done works on Chrome, on all the Android devices I tried with and with all of the iPhones on iOS 13 but doesn't work on older devices. I tried to do it in different ways, and none of them works... Here is my code if anyone wants to help!

  const wrapperRef = useRef(null)
  const [scrollToggle, changeScrollToggle] = useState(false)
  const alteredPriceRange = [defaultValue].concat(priceRange)

  const slideToggle = () => {
    if (scrollToggle) {
      changeScrollToggle(false)
    } else {
      changeScrollToggle(true)
    }
  }

  const handleClickOutside = (e) => {
    if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
      changeScrollToggle(false)
    } else {
    }
  }

  const optionSorter = (price) => {
    return price === 'min' || price === 'max' ? <>{`No ${price}`}</> : <><span>£</span> {price}</>
  }

  const selectedInput = () => {
    return currentValue === '' ? defaultValue : currentValue
  }

  useEffect(() => {
    const setHeight = slideRef => {
      if (scrollToggle) {
        slideRef.current.style.height = `${slideRef.current.firstChild.getBoundingClientRect().height}px`
      } else {
        slideRef.current.style.height = 0
      }
    }
    setHeight(slideRef)
  }, [scrollToggle])

  useEffect(() => {
    document.addEventListener('mousedown', handleClickOutside)
    return () => {
      document.removeEventListener('mousedown', handleClickOutside)
    }
  }, [])

  return (
    <Fragment>
      <div ref={wrapperRef} className={`dropdown-wrapper ${scrollToggle ? 'dropdown-active' : ''}`}>
        <div
          id={`dropdown-selector-${idValue}`}
          className='dropdown-field'
          onClick={() => { slideToggle() }}
        >
          <p>
            <span className='currency'>£</span><span className='money'>
              {selectedInput()}
            </span>
          </p>
          <span className='caret'>
            <img src={caret} alt='caret' />
          </span>
        </div>
        <div className='scroll-content-wrapper'>
          <div className='scroll-block' ref={slideRef}>
            <Picklist className={'area'}>
              {alteredPriceRange.map((price, i) =>
                <div
                  id={`${idValue}-price-option-${i}`}
                  key={i}
                  onClick={(e) => {
                    selectClick(e, price, defaultValue)
                    slideToggle()
                  }}
                >
                  {optionSorter(price)}
                </div>
              )
              }

            </Picklist>
          </div>

        </div>
      </div>
    </Fragment>
  )
}

Thank you


Solution

  • Try to use touchstart event

    The touchstart event occurs when the user touches an element.

    Note: The touchstart event will only work on devices with a touch screen.

      const handleClickOutside = (e) => {
        if (e.type === "touchstart") e.preventDefault();  // <- new line
        if (wrapperRef.current && !wrapperRef.current.contains(e.target)) {
          changeScrollToggle(false)
        } else {
        }
      }
    
      // ...
    
      useEffect(() => {
        document.addEventListener('touchstart', handleClickOutside)  // <- new line
        document.addEventListener('mousedown', handleClickOutside)
        return () => {
          document.removeEventListener('touchstart', handleClickOutside)  // <- new line
          document.removeEventListener('mousedown', handleClickOutside)
        }
      }, [])
    

    NOTE: Didn't test this code!