Inject a special javascript that basically overrides all your relevant framework methods and keeps a counter of the number of uncompleted requests, which is incremented upon start and decremented upon completion. Due to the asynchronous nature of javascript, it's not sufficient to keep a Boolean variable, you're likely to end up with race conditions within your own logic.
If you are using visual effects, you most likely want to do this for visual effects too; morphing/fading and other visuals will have a similar drawback.
So we primarily do a regular "click" then always wait for this counter to reach zero again, using javascript. After we got this in place we reduced transient issues to none.
Remember that selenium doesn't return from "click" till all the synchronous are processed, thus you have to be certain to get those counters incremented as a direct consequence of onclick.
For instructions on how to override framework methods you need to consult your framework documentation; we use addMethods in the prototype. It's possible to keep this overriding in a special javascript that is only added to the page when running tests.
If you are interested to learn Selenium on a much deeper level and want to become a professional in the testing domain, check out Intellipaat’s Selenium training course!