How to make keypress add a CSS class to text

PennController for IBEX Forums Support How to make keypress add a CSS class to text

Tagged: 

Viewing 11 posts - 1 through 11 (of 11 total)
  • Author
    Posts
  • #10611
    Larissa_Cury
    Participant

    Hello, 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 column

    So, 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!

    #10612
    Larissa_Cury
    Participant

    To 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 Chidiebere

    PRESS SPACEBAR:

    Jason Raheema Chassity
    >Oscar< Bryce Carlito
    Maleeka Nasreen Maureene
    Janet Hishaam Chidiebere

    4) 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 Chidiebere

    PRESS SPACEBAR

    Jason >Raheema< Chassity
    Oscar Bryce Carlito
    Maleeka Nasreen Maureene
    Janet Hishaam Chidiebere

    #10614
    Jeremy
    Keymaster

    Hi,

    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

    #10617
    Larissa_Cury
    Participant

    Thank 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!

    #10629
    Larissa_Cury
    Participant

    Hi, @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!

    #10631
    Jeremy
    Keymaster

    Hi 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 work

    Part 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 class

    Part 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 in callback won’t be run with an index that would be out of bounds

    3) getVar("listOfWords").set(getVar("highlightedWord")) sets the value of “listOfWords” to the same value as “highlightedWord”, ie. the current index. The next set 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 the highlighted class only for the word at the current index

    The CSS rule on .word will apply to each individual span inside the Text element’s DOM node; using the .css command on the Text element affects the whole node containing all the span elements, it doesn’t affect each span individually; using the .cssContainer command would be even worse, so to speak, because it would apply yet another level up the DOM hierarchy

    Re. the heights and widths, I refer to the ones where I commented “3 times”: just calculate 4 times instead

    Jeremy

    #10633
    Larissa_Cury
    Participant

    Hi, 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) ?

    #10638
    Jeremy
    Keymaster

    Hi Larissa,

    Instead of using .css you could indeed add the rules in the CSS file under .PennController-words { and the result would be equivalent

    The current rule in the CSS file targets .PennController-words .word, that is, elements with the class word that are children of elements with the class .PennController-words. We make sure to add the class word to each span inside the Text element (see the map function) so that selector does target each word individually, as intended

    Jeremy

    #10639
    Larissa_Cury
    Participant

    All right! Thank you very much for taking the time to explain that to me!

    #10942
    Larissa_Cury
    Participant

    Hi, @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
                )
            )
    
    #10946
    Larissa_Cury
    Participant

    Basically, 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.

Viewing 11 posts - 1 through 11 (of 11 total)
  • You must be logged in to reply to this topic.