Check what text has been highlighted by user

Hi community!

Is there an easy way to check which text / if a specific piece of text has been highlighted by user?

Example use case:

A div or prepopulated text field provides a certain text. User is asked to highlight a specific part of this text. In the moment the user has finished highlighting, the highlighted text is passed to a variable for further processing.

Thank you!

1 Like

You can achieve the result you want using a couple of experimental JavaScript technologies.
a. A Selection object, accessable via the window.getSelection() function.
b. the same type of object, accessed via the document.getSelection() function.
c. The selection property of the document object.

The following example demonstrates one way to use the above experimental technologies.

<<set $selection to ''>>\
<div id="the-text">Lorem ipsum dolor sit amet consectetur adipisicing elit. Illo culpa suscipit blanditiis nam odit itaque.</div>

<<timed 0s>>
	<<script>>
		$('#the-text').on('mouseup', function () {
			var selectedText = '';

			if (window.getSelection) { 
				selectedText = window.getSelection().toString();
			} 
			else if (document.getSelection) { 
				selectedText = document.getSelection().toString();
			} 
			else if (document.selection) { 
				selectedText = document.selection.createRange().text;
			}
			else {
				console.log('selection not supported');
				return;
			}

			console.log('selectedText: ' + selectedText);

			if (selectedText) {
				/* Update the $selected Story Variable. */
				State.variables.selected = selectedText;
			}
		});
	<</script>>
<</timed>>

see:
SugarCube: the <<timed>> macro, the <<script>> macro, and the State.variables collection object
jQuery .on() function.

2 Likes

Thank you very much! Code works perfectly (tested in FF and Edge). But can I bug you again? I tried turning your code into a widget for processing logic in the same passage, like checking if highlighted selection is correct, etc. As I do not know how to do this in JavaScript, I tried to use your code within ChapelR’s event macro.

> <<widget "checkHighlighted">>
> 	<<set _sentence to $args [0]>>
> 	<<set _correctHighlight to $args [1]>>
> 	<<set $selection to ''>>\
> 	<<print '<div id="highlightHere">' + _sentence + '</div>'>>
> 
> 	<<event 'mouseup' '#highlightHere'>> 
> 		<<script>>
> 				var selectedText = '';
> 
> 				if (window.getSelection) { 
> 					selectedText = window.getSelection().toString();
> 				} 
> 				else if (document.getSelection) { 
> 					selectedText = document.getSelection().toString();
> 				} 
> 				else if (document.selection) { 
> 					selectedText = document.selection.createRange().text;
> 				}
> 				else {
> 					console.log('selection not supported');
> 					return;
> 				}
> 
> 				console.log('selectedText: ' + selectedText);
> 	
> 				if (selectedText) {
> 					/* Update the $selected Story Variable. */
> 					State.variables.selection = selectedText;
> 				}
> 			});
            <</script>>
> 	    <<run UI.alert($selection)>>
> 	<</event>>
> <</widget>>

Upon highlighting, I get an alert window, but it is empty. Also, if I highlight a word by double-clicking on it, alert is opened and closed immediately. Is there a way to prevent this?

Thank you!

You have a few errors in your edited JavaScript:

  1. You may only return from a function, so when you attempted to change it from a function you introduced an error there.
  2. You didn’t remove the closing curly brace, parenthesis, and semi-colon of the jQuery event handler call.

Additionally. You have some general issues with your widget:

  1. You should probably have everything except the <div> within a <<silently>>.
  2. You do not need to <<print>> the <div>.
  3. You’re adding an event handler each time you call the widget, but not scheduling its removal. You either need to remove it upon navigation—what I’ve done in the following example—or remove the handler from the widget and use a global delegated handler instead.

Try something like the following:

<<widget "checkHighlighted">>
	\<<silently>>
		<<set _sentence to $args[0]>>
		<<set _correctHighlight to $args[1]>>
		<<set _highlightId to 'highlightHere'>>
		<<set $selection to ''>>

		<<event 'mouseup.checkHighlighted' `'#' + _highlightId`>> 
			<<script>>
			var selectedText = '';

			if (window.getSelection) { 
				selectedText = window.getSelection().toString();
			} 
			else if (document.getSelection) { 
				selectedText = document.getSelection().toString();
			} 
			else if (document.selection) { 
				selectedText = document.selection.createRange().text;
			}
			else {
				console.log('selection not supported');
			}

			if (selectedText) {
				/* Update the story variable: $selection. */
				State.variables.selection = selectedText;
			}
			<</script>>
			<<if $selection is _correctHighlight>>
				<<run UI.alert('Correct: ' + $selection)>>
			<<else>>
				<<run UI.alert('Wrong: ' + $selection)>>
			<</if>>
		<</event>>
		<<event ':passageinit'>>
			/* Remove our `mouseup` event handler on navigation. */
			<<run $(document).off('mouseup.checkHighlighted')>>
		<</event>>
	<</silently>>
	\<div @id="_highlightId">_sentence</div>
\<</widget>>

NOTES:

  • I added an example test against _correctHighlight after the <<script>>.
  • In your final code, you may wish to trim the selected text, so that leading/trailing whitespace is not included—since it’s fairly easy to accidentally select some.

 

Only by not making the mouseup event handler not handle the selection test itself or by not triggering on that event.

As to why. Double-clicking fires several mouse events beyond the dblclick event—specifically, two pairs of mousedown/mouseup events and two click events.

 
EDIT (×1): Updated the widget.

1 Like

Thank you so much. I had time to use and try your widget and it works exactly as intended. But upon usage, I encountered problem: I need to call widget on several sentences/highlight. I did so by leaving passage and reentering with new arguments. But this messes up my timer (as it begins counting on passage load). So I tried to adapt your widget to run through array of sentences and also store text to be highlighted in the very sentence.
I have e.g. the following in my StoryInit:

<<set $sentences = ["As early as 1835, -Ada Lovelace* developed the first complex program for a mechanical calculating machine.",
"In 1882 -Maria Beasley* revolutionized seafaring by inventing the lifeboat.", "Without them we would have drunk our coffee very differently for decades: -Melitta Bentz* invented the coffee filter in 1908.", "-Marie Curie* discovered the two radioactive elements polonium and radium and has won two Nobel Prizes.", "An adjustable central gas heating system that was later used to heat thousands of public buildings and households was invented in 1919 by -Alice H. Parker*.", "-Gertrude Belle Elion* received the Nobel Prize in Medicine for her research in the field of chemotherapy and the invention of the drug mercaptopurine.", "It was only with her program that the first moon landing was possible: -Margaret Hamilton* was a senior software engineer at NASA.", "Many know -Hedy Lamarr* as an actress, but it was her invention of frequency hopping that made technologies such as WiFi and GPS possible in the first place.", "The Polish scientist -Stephanie Kwolek* invented the synthetic fiber Kevlar, which is five times stronger than steel.", "-Maria Telkes* has done significant research in the field of solar energy.", "The Egyptian ruler - Cleopatra* rose to be ruler of the Orient alongside Caesar and Marc Anthony.", "-Jeanne d’Arc* led the soldiers against the English in the Hundred Years War.", "-Catharina the Second* ousted her husband from the throne and had herself proclaimed Russian Tsarina.", "-Rosa Luxemburg *was the founder of the Spartakusbund and the Communist Party and tried to bring about the end of the First World War with mass strikes.", "-Ayn Rand* was born in Russia and is the founder of objectivism.", "-Simone de Beauvoir* had an important influence on feminism and existentialism.", "One of the most important philosophers of the 20th century is -Hannah Arendt*, who has dealt with totalitarianism, among other things."]>>
<<set $sentenceCount to $sentences.length>>
<<set $highlightCounter to 0>>
<<set $correctHighlighted to 0>>
<<set $selectedSentences to []>>

<<for _i to 0; _i lt 8; _i++>>
	<<set $selectedSentences.push($sentences.pluck())>>
<</for>>

("-" and “*” before and after names are to later separate fragment to be highlighted.)

Then I added some lines in front of your widget to fetch data from array, separate fragment and do the rest:

<<widget "checkHighlighted">>
	<<set _sentence to "">>
	<<set _correctHighlight to "">>
	<<if $selectedSentences.length gt 0>>
		<<set _currentSentence to $selectedSentences.pluck()>>
		<<set _fullSentence to _currentSentence.replace("-", "").replace("*", "")>>
		<<set _mark to _currentSentence.split('-').pop().split('*')[0]>>
	<<else>>
		<<goto "levelFinished">>
	<</if>>
	<<silently>>
		<<set _sentence to _fullSentence>>
		<<set _correctHighlight to _mark>>
		<<set _highlightId to 'highlightHere'>>
		<<set $selection to ''>>
		<<event 'mouseup.checkHighlighted' `'#' + _highlightId`>> 
			<<set $highlightCounter++>>
			<<script>>
			var selectedText = '';
			if (window.getSelection) { 
				selectedText = window.getSelection().toString();
			} 
			else if (document.getSelection) { 
				selectedText = document.getSelection().toString();
			} 
			else if (document.selection) { 
				selectedText = document.selection.createRange().text;
			}
			else {
				console.log('selection not supported');
			}

			if (selectedText) {
				/* Update the story variable: $selection. */
				State.variables.selection = selectedText;
			}
			<</script>>
			<<if $selection is _correctHighlight>>
				<<set $correctHighlighted ++>>
				<<audio "ping" play>>
				<<remove "#highlightHere">>
				<<run UI.alert("length:"+ $selectedSentences.length)>>
				<<checkHighlighted>>
			<<else>>
				<<audio "wrong" play>>
				<<remove "#highlightHere">>
				<<checkHighlighted>>
			<</if>>
		<</event>>
		<<event ':passageinit'>>
			/* Remove our `mouseup` event handler on navigation. */
			<<run $(document).off('mouseup.checkHighlighted')>>
		<</event>>
	<</silently>>
	<div @id="_highlightId">_sentence</div>
<</widget>>

The first part (separating part to be highlighted from sentence) works and then one sentence is displayed. Upon highlighting, success is detected, but no further sentences are loaded.

Could you help me figure out what I am missing?

Sorry again. Could somebody help me find solution for my problem? I think it must have to do with removing elements when finishing, I thought

<<remove "#highlightHere">>

would do the trick, but then next sentence is not displayed, but should be, as array still holds sentences?

Sorry for being such a pain in backside, but could somebody have a look at my issue? Thank you all in advance!