PennController for IBEX › Forums › Support › How to make keypress add a CSS class to text
Tagged: css class
- This topic has 10 replies, 2 voices, and was last updated 1 year, 4 months ago by
Larissa_Cury.
-
AuthorPosts
-
May 23, 2023 at 1:47 pm #10611
Larissa_Cury
ParticipantHello, everyone! I’m trying to come up with a logic to do the following:
1) Displany n columns of words on the screen at the same time as the words are displayed in the csv file
2) Highlight the first word (I thought of a CSS class to do it)
3) When participant hits spacebar, the highlight goes to the next word below
4) When the participants finishes the columns, the highlight class moves up to the first word of the next columnSo, I thought that the simpliest way to do it avoid hardcoding each word myself or using pictures was to have a CSS border class to highlight the target word in question and, when participant hits spacebar, then the class moves up to the next word. How could we do that in Pcibex?
I’ve put an example of a .csv with three word columns in this project: https://farm.pcibex.net/r/xffSYC/
PS: I’ve added an image to the project to illustrate what I have to achieve! 🙂Thanks in advance!
May 23, 2023 at 1:56 pm #10612Larissa_Cury
ParticipantTo illustrate it:
# Suppose >< represents a CSS border class to highlight the words:
1) Display n columns of words on the screen at the same time as the words are displayed in the csv file
2) Highlight the first word (I thought of a CSS class to do it)
3) When participant hits spacebar, the highlight goes to the next word below>Jason< Raheema Chassity
Oscar Bryce Carlito
Maleeka Nasreen Maureene
Janet Hishaam ChidieberePRESS SPACEBAR:
Jason Raheema Chassity
>Oscar< Bryce Carlito
Maleeka Nasreen Maureene
Janet Hishaam Chidiebere4) When the participants finishes the columns, the highlight class moves up to the first word of the next column
Jason Raheema Chassity
Oscar Bryce Carlito
Maleeka Nasreen Maureene
>Janet< Hishaam ChidieberePRESS SPACEBAR
Jason >Raheema< Chassity
Oscar Bryce Carlito
Maleeka Nasreen Maureene
Janet Hishaam ChidiebereMay 24, 2023 at 10:21 am #10614Jeremy
KeymasterHi,
First let me bring your attention to what I said at the beginning of this message, repeated here:
Template scans the table and produces one output per row. So the idea is, you use Template( row => newTrial to generate one trial per row. Listing one word per row is not the ideal way to go if you want to display several of them at the same time on the page, ie. during the same trial
If you want to display several words on one screen, listing them on different lines in a CSV file is not the way to go
One way to implement what you describe is to proceed very similarly to what we discussed in the other thread: instead of incrementing the value of the Var element named “nextWordStartsAt” by 10 on each keypress and replacing the text of the Text element named “words” with the next 10 words, you increment the value by 1 each time and replace the text with the same list of words each time, just edited to place an HTML tag around the next word each time
So it could look something like this, assuming you always list 9 words in your table’s “Words” column (you could do 12 words as in your example too, just adjust the widths and heights accordingly to fit 4×4 words):
// embed each word in a span, highlight the very first one newText("words", row.Words.split('_').map((w,i)=>`<span class='word ${i==0?'highlighted':''}'>${w}</span>`).join('')) .cssContainer("width","810px") // 3 times the space reserved for each word + horizontal padding .css({ display: 'flex', // items contained in the element will be rearranged smartly 'flex-direction': 'column', // stack items in columns 'flex-wrap': 'wrap', // items that overflow vertically will start on new columns 'font-size': '50px', height: '210px' // more than 3 times the space reserved for each word + vertical padding }) .print() , newVar("listOfWords", ""), // we'll update this Var element with the list of words tagged as desired newVar("highlightedWord").set(0) // this is the index of the highlighted word , newKey("NEXT", " ") .callback( // first make sure that the next index is within bounds getVar("highlightedWord").test.is(v=>v+1<row.Words.split('_').length).success( getVar("highlightedWord").set(v=>v+1) // increment the index of the highlighted word , getVar("listOfWords") // update listOfWords with appropriate tags .set(getVar("highlightedWord")) // first look up the index .set(v=>row.Words.split('_').map((w,i)=>`<span class='word ${i==v?'highlighted':''}'>${w}</span>`).join('')) // now set the class as desired , getText("words").text( getVar("listOfWords") ) // update the content of the Text element ) )
In global_main.css, you add these rule:
.PennController-words .word { width: 230px; height: 60px; padding: 5px 15px; } .PennController-words .word.highlighted { outline: solid 10px yellow; border-radius: 0.25em; }
Just make sure the width you pass in your script to
.cssContainer
is at least 3 times (the width + 2*padding) and the height you pass to.css
is at least 3 times (the height + 2*padding), that you set in the file’s CSS rule for.word
.Jeremy
May 24, 2023 at 2:43 pm #10617Larissa_Cury
ParticipantThank you VERY much, Jeremy! I have a few questions, but I’ll work on my own to try to solve them or to come up with more elaborated questions. Thank you very much, this is amazing!
May 25, 2023 at 8:07 am #10629Larissa_Cury
ParticipantHi, @Jeremy! Can you help me walk through it?
# PART 1:
// embed each word in a span, highlight the very first one newText("words", row.Words.split('_').map((w,i)=><code><span class='word ${i==0?'highlighted':''}'>${w}</span></code>).join(''))
1) We create newText called “words” which gets each word from the cell. All words are separated by “_”, hence, “a word” equals “a string separed by _”, which is what split(”) is doing
2) Involve the words in a span HTML class (I’m a bit confused here, but I guess it is to show the words in a box-like way?)
3) the map function gets the current element (each word (w)) and the current index (i). The condition says “if the current index equals 0, add the highlighted class to .word otherwise, just print the word {w}, which just has the class .word
4) Join the words back together in a string separed by the empty string# part 2:
.cssContainer("width","810px") // 3 times the space reserved for each word + horizontal padding .css({ display: 'flex', // items contained in the element will be rearranged smartly 'flex-direction': 'column', // stack items in columns 'flex-wrap': 'wrap', // items that overflow vertically will start on new columns 'font-size': '50px', height: '210px' // more than 3 times the space reserved for each word + vertical padding }) .print()
I was a bit confused here with the two css elements, the .cssContainer and the .css. At first, I supposed that the .cssContainer would modify the NewText element and the .css would modify the .word or .word highlighted classes, but if I change the font-size, it changes the fontsize of the text element, so I guess I’m wrong. Hence: Why do we have both .css and .cssContainer here?
Second question: “3 times the space reserved for each word + horizontal padding”. I couldn’t figure out waht “the space reserved for each word” means. Are we talking about the specifications in the .word class in the css file? (if yes, what is precisely the “space” reserved? the padding?)
# PART 3:
newVar("listOfWords", ""), // we'll update this Var element with the list of words tagged as desired newVar("highlightedWord").set(0) // this is the index of the highlighted word , newKey("NEXT", " ") .callback( // first make sure that the next index is within bounds getVar("highlightedWord").test.is(v=>v+1<row.Words.split('_').length).success( getVar("highlightedWord").set(v=>v+1) // increment the index of the highlighted word , getVar("listOfWords") // update listOfWords with appropriate tags .set(getVar("highlightedWord")) // first look up the index .set(v=>row.Words.split('_').map((w,i)=><code><span class='word ${i==v?'highlighted':''}'>${w}</span></code>).join('')) // now set the class as desired , getText("words").text( getVar("listOfWords") ) // update the content of the Text element ) )
1) I didn’t get the ‘check’ logic: getVar(“highlightedWord”).test.is(v=>v+1<row.Words.split(‘_’).length).success() ? Shouldn’t we test if i === 0 ? why then v + 1 ?
2) Ok, we increment the word index by one (to move on to the next word)
3) I get this part:.set(v=>row.Words.split('_').map((w,i)=>
<span class=’word ${i==v?’highlighted’:”}’>${w}</span>).join('')) // now set the class as desired
if the current word is === v, then add the highlight class, otherwise, just print the w , right?But I can’t understand the 2 lines before it.
4) we have to update the textElement, so we update the content of ‘words’ with the ‘list of words’ one.# Questions about the styling:
I also didn’t quite understand what css was updating what element, if I got it right, then:
# Updates the ‘words’ in general (all words) – then, why do we need the cssContainer back there?
.PennController-words .word { width: 230px; height: 60px; padding: 5px 15px; # around each word }
.word.highlighted
this is ok, this is the highlighted class 🙂When you mentioned “you could do 12 words as in your example too, just adjust the widths and heights accordingly to fit 4×4 words” which height and weight were you refering too? I changed the height of the .css (originally 210pc), but I don’t know if that was the right place to do so?
Sorry for the long post, I started learning ‘raw’ Js and now I guess I’m a little bit more able to understand PciBex code better, I really would like to understand it. Thanks in advance!
May 26, 2023 at 10:47 am #10631Jeremy
KeymasterHi Larissa,
Part 1: You got it; we embed each word in a
span
so that the flex display can render them as distinct elements, as otherwise adding them as simple text nodes wouldn’t workPart 2: You can read this documentation page to see how PennController elements are rendered in the DOM.
cssContainer
affects the container element (the one with the.PennController-Text-container
class),.css
affects the element itself (the one with the.PennController-Text
class). The space reserved for each word is the width you set for the.word
classPart 3:
1) If you tested for 0, then you would only run the code in
callback
once when the index is still 0, ie. when pressing space when the first word is highlighted, and then it wouldn’t run again on later key presses, because the value would be greater than 0. The test is just here to make sure that the code incallback
won’t be run with an index that would be out of bounds3)
getVar("listOfWords").set(getVar("highlightedWord"))
sets the value of “listOfWords” to the same value as “highlightedWord”, ie. the current index. The nextset
command uses a function whose parameter represents the current value of the Var element, so we can refer to it in the body of the function: because we just set “listOfWords” to the current index,v
in the body of the function refers to the current index, and${i==v?'highlighted':''}
will therefore include thehighlighted
class only for the word at the current indexThe CSS rule on
.word
will apply to each individualspan
inside the Text element’s DOM node; using the.css
command on the Text element affects the whole node containing all thespan
elements, it doesn’t affect eachspan
individually; using the.cssContainer
command would be even worse, so to speak, because it would apply yet another level up the DOM hierarchyRe. the heights and widths, I refer to the ones where I commented “3 times”: just calculate 4 times instead
Jeremy
May 27, 2023 at 2:11 pm #10633Larissa_Cury
ParticipantHi, Jeremy! Things are much clearer now, thank you!!
I’ve read the documentation link you’ve sent me, thanks, but I’m still wondering about one thing:
If in the doc’s example,
newText("dots", "...").print()
.dots { text-decoration: underline; }
equals
newText("dots") .css("text-decoration","underline") .print()
Then how come the the separed .css manipulation for “words” in the separed file be different from the one we have with the
.css
in the code? From what I got from the doc, the.css
in the code points to the element itself (in the ex case, “dots”, in my case “word”), right? Hence, how can the separed one refer to all individual span classes and the .css one refer to all span classes, shouldn’t they both point to “words” ? (the element to each they’re attached to) ?May 30, 2023 at 10:13 am #10638Jeremy
KeymasterHi Larissa,
Instead of using
.css
you could indeed add the rules in the CSS file under.PennController-words {
and the result would be equivalentThe current rule in the CSS file targets
.PennController-words .word
, that is, elements with the classword
that are children of elements with the class.PennController-words
. We make sure to add the classword
to eachspan
inside the Text element (see themap
function) so that selector does target each word individually, as intendedJeremy
May 30, 2023 at 1:09 pm #10639Larissa_Cury
ParticipantAll right! Thank you very much for taking the time to explain that to me!
November 5, 2023 at 10:28 am #10942Larissa_Cury
ParticipantHi, @Jeremy ,
I’m trying to adapt this code following the same rationale, but I’m struggling with that, I cannot display 10 words and make the highlight isolate single words.newText("words", row.Words.split('_').slice(0,10).join("<br>")) // use slice to get the 10 first words .cssContainer({ 'line-height': '1.5', padding: '0.5em', // 0.5 'text-align': 'center', "justify-content": 'center', "align-items": 'center' , 'font-size': '50px', 'font-family': 'Arial' }) .print() , newVar("listOfWords", ""), // we'll set this Var element to the next list of words newVar("nextWordStartsAt").set(10) // this is the index where the next 10 words start , newKey("NEXT", " ") .callback( // first make sure that the index is within bounds getVar("nextWordStartsAt").test.is(v=>v<row.Words.split('_').length).success( // set listOfWords to the next words getVar("listOfWords") .set(getVar("nextWordStartsAt")) // first look up the index .set(v=>row.Words.split('_').slice(v,v+10).join("<br>")) // then use the index+slice to get the next 10 words , getText("words").text( getVar("listOfWords") ) // update the content of the Text element , getVar("nextWordStartsAt").set(v=>v+10) // the next list of 10 words starts 10 words further ) )
November 6, 2023 at 8:15 pm #10946Larissa_Cury
ParticipantBasically, what I mean is: I cannot use the logic from the previous code on this one. In this code, I have a list of words separated by _ in a single row in a column called ‘Words’. The words are slipt into 10 chunks each. I’d like to highlight the single words by the keypress on the spacebar, but I cannot do it. I’d like to make the highlighted word appear and when the word in the tenth in the column, move on to the next one.
-
AuthorPosts
- You must be logged in to reply to this topic.