I wanted to implement a JavaScript onMouseMove Event, that translates the word under the mouse pointer with the Google Translator. I want to share the following solution.
I split this into the following two separate problems:
First: Detect word for an onMouseMove Event
The onMouseMove Event can listen on any Element for mouse movements. To detect the word under the mouse pointers position, I utilize the W3C(DOM-2) specified Range Object.
Most modern browsers support the Range object, but the event.rangeParent Attribute seems to be only supported by Firefox. Currently I’ve no cross-browser solution for this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 | /** * Apply offset to range and extract a single character at position. * * range -- the range object effected * originalOffsetStart, originalOffsetEnd -- Start offsetStart and offsetEnd * offset -- the relative offset to apply */ function getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, offset) { var character = ' '; try { range.setStart(range.startContainer, originalOffsetStart + offset); range.setEnd(range.startContainer, originalOffsetEnd + offset); character = range.toString()[0]; range.setStart(range.startContainer, originalOffsetStart - offset); range.setEnd(range.startContainer, originalOffsetEnd - offset); } catch (e) {} return character; } /** * Returns a single word for the given event. */ function getEventWord(evt) { if (!evt.rangeParent || !document.createRange) { return ''; } var range = document.createRange(); range.setStart(evt.rangeParent, evt.rangeOffset); range.setEnd(evt.rangeParent, evt.rangeOffset); // the word ends when this characters appears var stop_character_pattern = /^[\W]$/m; // this "overwrites" some characters from the above pattern var ignore_stop_character_pattern = /^['|\-]$/; // I assume startOffset == endOffset so set 1 character selection ... var originalOffsetStart = range.startOffset; var originalOffsetEnd = range.startOffset + 1; try { // test: range.setStart(range.startContainer, originalOffsetStart); range.setEnd(range.startContainer, originalOffsetEnd); } catch (e) { // out of bounds originalOffsetStart -= 1; originalOffsetEnd -= 1; range.setStart(range.startContainer, originalOffsetStart); range.setEnd(range.startContainer, originalOffsetEnd); } // First Step: Find left end of word: var leftCharacterPos = 0; var iChar = ''; for (var iOffStart = 0; iOffStart <= originalOffsetStart; iOffStart++) { iChar = getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, -1 * iOffStart); leftCharacterPos = iOffStart; if (stop_character_pattern.test(iChar) && !ignore_stop_character_pattern.test(iChar)) { // remove the stop character! leftCharacterPos -= 1; break; } } leftCharacterPos = originalOffsetStart - leftCharacterPos; // Last Step: Find right end of word: var rightCharacterPos = 0; for (iOffStart = 0; true; iOffStart++) { iChar = getRangeCharacter(range, originalOffsetStart, originalOffsetEnd, iOffStart); if (stop_character_pattern.test(iChar) && !ignore_stop_character_pattern.test(iChar)) { rightCharacterPos = iOffStart - 1; break; } } rightCharacterPos = originalOffsetEnd + rightCharacterPos; try { range.setStart(range.startContainer, leftCharacterPos); range.setEnd(range.startContainer, rightCharacterPos); } catch (e1) { range.detach(); return ''; } var retWord = range.toString(); range.detach(); return retWord; } |
Second: Translate Text with Google Translator
This is very easy because Google provides an very easy to use Ajax API for this (attend the possible google user tracking!):
1 | <script type="text/javascript" src="http://www.google.com/jsapi"></script> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | // read onMoveTranslate() var last_word = null; var timeout = null; var word_cache = []; /** * Translate text For onmousemove events. */ function onMoveTranslate(event) { // get element the translation should appear in: var translation = document.getElementById('translation'); var word = getEventWord(event); if (!word || word == '') { return; } if (typeof(word_cache[word]) !== 'undefined') { translation.innerHTML = word_cache[word]; //+" (cached)"; return; } if (word != last_word) { if (timeout) { window.clearTimeout(timeout); } translation.innerHTML = 'Translating ... ' + word; timeout = window.setTimeout(function(){ google.language.translate(word, 'en', 'de', function(result) { if (!result.error) { translation.innerHTML = result.translation; word_cache[word] = result.translation; } }); }, 800); } last_word = word; } google.load("language", "1"); |
At last I assign the onMouseMove Event to the onMoveTranslate() function:
1 | <div id="content" onmousemove="onMoveTranslate(event);"> |