To understand this problem, first you have to understand why we hash passwords. It is completely possible to store a password in plain text on a server and simply compare the password transmitted to the password received. As long as the password is protected in transit, this is a secure means of authentication (shared secret).
The reason that passwords are hashed is because the problem isn't the authentication, but the storage. If the server is ever compromised, the attacker would immediately have access to all user accounts as they would now know the secret used for authentication of the users.
Hashing acts as a barrier to this. Since the server doesn't know the actual input required to authenticate, even a compromise to the DB does not grant an attacker access to the user accounts. They would still need to figure out the input to give to reach the hash values the application checks against. Sure they could alter all the values to something they know, but this would rapidly throw up suspicion and the system would be shut down and secured.
So, the problem with client side hashing is that it effectively makes the result of the hash the password rather than the password. There is nothing to stop an attacker from bypassing the official client and simply sending the finished hash to the server directly. It provides no additional (or loss) of security during the authentication, but under the situation that hashing is designed to protect against, it offers nothing since the hash stored in the DB is actually the shared secret transmitted to the server.
That said, there are two notable thing client side hashing does give you. While it doesn't help protect your system at all, it may help protect your user. If you are insecurely transmitting the password or the transmission gets compromised without the client code getting compromised, you will still protect the user's password (which they may reuse on other sites) from being leaked.
The other is that you can provide additional iterations of a hash to make an offline attack against the DB more difficult (or near impossible if the hash output is long enough) without having to use server cycles (effectively extending the length of the "intermediate" password that the client submits), but you still need sufficient server cycles to protect the lengthened password against a rogue client. It's still pretty limited in how many effective characters it adds. According to the math I did, a 1 second slow hash, even implemented efficiently by the browser in native code such that nobody could hash it faster than that, would only add about 6 random characters of entropy to the password, and getting 7 characters of entropy would require about a 100 second slow hash.
Put another way, while it does provide some protections, from the point of view of the server, the client side hash should be treated as if it was the user's direct password. It provides no more or no less security on the server than if the user had directly given their password and should be protected as such.
If you want to be able to provide that extra level of security, I would recommend two hashes. Hash once client side to build a new, unique password, then hash that password on the server to make a value you store in the DB. This way you get the best of both worlds.
For the most part, SSL is trusted sufficiently to protect the exchange that the initial hash prior to transmission is seen as unnecessary though and a compromised server could always alter the code sent to the client such that the initial hash isn't performed. It simply isn't an effective alternative to SSL and doesn't offer enough additional advantage to be worth the costs and complexity.