Block explorers I've seen typically accept the following inputs:
Bitcoin address: 25-34 alphanumeric characters.
Block height: decimal number, up to let's say 10 digits
Block hash: 64 hex digits (0-9, a-f), representing a 256-bit number
Transaction ID: 64 hex digits
So it's easy to recognize a Bitcoin address or block height with a simple regex.
They can't tell apart a block hash from a transaction ID, because in principle any given 256-bit number could be the hash of either (or neither). So when a string of 64 hex digits is input, they simply search for it as block hash and as a transaction ID, and if one of them is found, they return the appropriate information. It's overwhelmingly improbable that the same string could be the hash of a valid block and of a valid transaction, so there isn't really any need to handle the possibility that both are found.
They could do some minor optimizations: for instance, if the string starts with a bunch of zeros, it's more likely to be a block hash, so maybe they search that table first.