Filled TextInput

PennController for IBEX Forums Support Filled TextInput

Viewing 15 posts - 1 through 15 (of 30 total)
  • Author
    Posts
  • #5189
    Lynn
    Participant

    Hello Jeremy,

    I have been trying to find some settings for the TextInput Elements in my code below :
    1) How can I make sure that the “Continue” button doesn’t take effect if some of the TextInput Elements are not filled ?
    2) Is there some way to adjust the TextInput answers to be strictly between 2 certain numbers ? i.e. : in the “age” TextInput below, if the number written is less than 18, it’s not validated.

        newText("age", "<p></p>Age (years):")
            .print()
        ,
        newTextInput("age")
            .settings.length(2)
            .print()
            .log()
        ,
        newText("height", "<p></p>Height (cm):")
            .print()
        ,
        newTextInput("height")
            .settings.length(3)
            .print()
            .log()
        ,
        newText("weight", "<p></p>Weight (kg):")
            .print()
        ,
        newTextInput("weight")
            .settings.length(3)
            .print()
            .log()
        ,
        newText("<p></p>")
            .print()
        ,
        newButton("Continue")
            .color("green")
            .print()
            .wait()

    Thank you so much in advance for your help.
    Sincerely,

    #5194
    Jeremy
    Keymaster

    Hello,

    Yes, you can use .test.text to check the content of a TextInput element, and doing so in the wait command will only validate the click if the test is successful. In your case, you can use regular expressions to check for appropriate numbers, just replace your very last .wait() line with this:

    .wait(
        getTextInput("age").test.text( /^(\d|1[0-8])$/ )
            .failure( newText("Age should be a number between 0 and 18").print() )
        .and( getTextInput("weight").test.text( /^\d+$/ )
            .failure( newText("Weight should be a numeric value").print() )
        ).and( getTextInput("height").test.text( /^\d+$/ )
            .failure( newText("Height should be a numeric value").print() )
        )
    )

    Jeremy

    #5196
    Lynn
    Participant

    Thank you so much for your fast reply !

    #6079
    daniela
    Participant

    Hi Jeremy,

    is there a way to adapt test.text() to test whether or not the textInput element isn’t empty? It seems like the most standard test for a textInput element, but I can’t seem to find the solution anywhere!

    Best,
    Daniela

    #6080
    Jeremy
    Keymaster

    Hi Daniela,

    You can take two approaches: either use a negative test testNot.text("") or a positive test using a regex test.text(/[^\s]+/). The latter is more powerful, as it will only validate non-empty text, whereas the former would succeed even if the text only consists of spaces, for example

    Jeremy

    #6126
    daniela
    Participant

    Hi Jeremy,

    that’s great, thank you again!

    – Daniela

    #6747
    kathy
    Participant

    Hi Jeremy,

    I added a TextInput (for age) to the demographics page of my experiment, restricted it to two characters and managed to add an error message if left empty. Now there are a few issues, though:

    1. None of the error messages on the page (including the ones of the DropDowns I had before) will disappear once options are selected / the TextInput is filled.
    2. The continue (“Weiter”) button doesn’t work anymore.
    3. I am also unsure where to add the restriction of numerical values for age to the TextInput.

    Here is the code:
    (I’m unsure about .wait(“first”) at the end – I actually prefer participants having to click continue once everything has been filled out, so I might change that still).

    //// DEMOGRAPHICS ==============================================================
    PennController("demographics",
        newText("DemographicsText", "<p>Bevor es losgeht, brauchen wir einige Angaben zu Deiner Person. Diese werden anonymisiert gespeichert und eine spätere Zuordnung zu Dir wird nicht möglich sein. Bitte nimm Dir beim Ausfüllen der Felder Zeit.<p>")              
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.italic()
    
        ,
    
        newCanvas("infocanvas", 1000, 70)
    
            .settings.add(0, 0, getText("DemographicsText") )
    
            .print()
        ,
        
        newTextInput("age")
            .settings.length(2)
            .settings.log()
        ,   
        newText("Alter", "1. Alter:")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        ,
        
        newCanvas("agecanvas", 1000, 45)
            .settings.add(0,10, getText("Alter"))  
            .settings.add(80,8, getTextInput("age"))
            .settings.log()
            .print()
        ,
        
     ///
        
        newText("Geschlecht", "2. Geschlecht: ")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        ,
        newDropDown("sex","")
            .settings.add("Weiblich","Männlich", "Divers")
            .settings.log()
        ,
        
        newCanvas("sexcanvas", 1000, 40)
            .settings.add(0,0, getText("Geschlecht"))
            .settings.add(120,3, getDropDown("sex"))
            .print()
        ,
     
     ///
        
        newText("Muttersprache", "3. Ist Deutsch Deine Muttersprache?")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        ,
        newDropDown("german", "")
            .settings.add("Ja", "Nein")
            .settings.log()
        ,
        
        newCanvas("Nativelangcanvas", 1000, 40)
            .settings.add(0,0, getText("Muttersprache"))
            .settings.add(295,3, getDropDown("german"))
            .print()
        ,
        
     ///
        
        newText("Nationality", "4. Was ist Deine Nationalität?")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        
        ,
        
        newDropDown("nationality", "")
            .settings.add("Deutschland","Österreich", "Schweiz", "Italien", "Andere")
            .settings.log()
            
        ,
        
        newCanvas("nationalcanvas", 1000, 40)
            .settings.add(0,0, getText("Nationality"))
            .settings.add(240,2, getDropDown("nationality"))
            .print()
        ,
        
    ///
        newText("Bildungsabschluss", "5. Was ist Dein höchster Bildungsabschluss?")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        ,
        
        newDropDown("bildung", "")
            .settings.add("(Pflicht-)Schulabschluss","Abitur / Matura oder gleichwertiger Abschluss", "Hochschulabschluss", "Ausbildung", "Sonstige")
            .settings.log()
            
        ,
        
        newCanvas("bildungcanvas", 1000, 40)
            .settings.add(0,0, getText("Bildungsabschluss"))
            .settings.add(350,2, getDropDown("bildung"))
            .print()
        ,
    
     ///
        
        newText("Hauptbeschäftigung", "6. Was ist Deine derzeitige Hauptbeschäftigung?")
            .settings.css("font-family","times new roman") .settings.css("font-size", "18px")
            .settings.bold()
        ,
        
        newDropDown("job", "")
            .settings.add("Keine","Studium","Anstellung", "Selbstständigkeit", "Sonstige")
            .settings.log()
            
        ,
        
        newCanvas("jobcanvas", 1000, 40)
            .settings.add(0,0, getText("Hauptbeschäftigung"))
            .settings.add(380,2, getDropDown("job"))
            .print()
        ,
    
     ///
        newButton("okay", "Weiter")
            .print()
            .wait()
        ,
        
        getTextInput("age")
        .test.text(/[^\s]/)
            .failure(
             newText("Bitte gib Dein Alter an.")
             .settings.color("red")
             .print())
       
        ,
        
        getDropDown("sex")
        .test.selected()
        .success()
        .failure(
            newText("Bitte gib Dein Geschlecht an.")
            .settings.color("red")
            .print())
        ,
        
        getDropDown("german")
        .test.selected()
        .success()
        .failure(
            newText("Bitte gib an, ob Deutsch Deine Muttersprache ist.")
            .settings.color("red")
            .print())
        ,
        
        getDropDown("nationality")
        .test.selected()
        .success()
        .failure(
            newText("Bitte gib Deine Nationalität an.")
            .settings.color("red")
            .print())
        ,
        
        getDropDown("bildung")
        .test.selected()
        .success()
        .failure(
            newText("Bitte gib Deinen Bildungsstand an.")
            .settings.color("red")
            .print())
        ,
        
        getDropDown("job")
        .test.selected()
        .success()
        .failure(
            newText("Bitte gib Deine Hauptbeschäftigung an.")
            .settings.color("red")
            .print())
        ,
        
        getTextInput("age").wait("first")
        ,
        getDropDown("sex").wait("first")
        ,
        getDropDown("german").wait("first")
        ,
        
        getDropDown("nationality").wait("first")
        ,
        getDropDown("bildung").wait("first")
        ,
        getDropDown("job").wait("first")
        ,
            getButton("okay")
            .print()
            
    )

    Sorry it’s a bit long! Thanks for your help in advance.

    Best,
    Kathy

    • This reply was modified 3 years ago by kathy.
    • This reply was modified 3 years ago by kathy.
    #6750
    Jeremy
    Keymaster

    Hi Kathy,

    Remember that the script is read and executed in a straightforward top-down fashion by PennController, and it will only pause on wait commands. So in your code above, all the elements are printed onto the page and then PennController stays on the Button element’s wait command until your participant clicks it. Once the button is clicked, all the test commands below are read and executed in a split second, and the next wait command that PennController reads is the TextInput’s wait command: if your participant did not press Enter while typing in the input box, they will have to press Enter while typing in that box so that PennController can continue reading the rest of the code. Then it will move on and read the next wait command, the one called on the “sex” DropDown element, and your participant will need to select an option from that list if they haven’t done so yet. Then the next wait command, and so on. (The last print command on the Button element will have no effect practically speaking, because the script will move on to the next trial immediately after it)

    I don’t think this is what you want. What you want is for PennController to read everything, stay on the Button element’s wait command and only validate it if some conditions are met, but stay on it if those conditions are not met. You do that by inserting test commands inside the wait command, just like I illustrated above.

    Regarding the restriction to numerical values, you simply replace the regular expression in your test with one that tests for numbers only, eg /^\d+$/.

    Regarding the disappearance of the error messages, this is something you’ll need to code yourself. DropDown elements have a callback command that you can use to execute commands whenever an option is selected. So if you give unique names to your error messages, you could use callback on each of your DropDown elements to remove the corresponding error Text element. There is no callback command on the TextInput element that can detect keypresses, so you’d have to find a workaround. One option would be to use callback on a Key element to detect any keypress (including outside the input box) and remove the error message: it would overgenerate but it wouldn’t be too bad.

    Here is a link to a project where I rearranged your code along those lines: https://farm.pcibex.net/r/SsqEZD/

    Let me know if you have any questions

    Jeremy

    #6756
    kathy
    Participant

    Hi Jeremy,

    thanks so much for your explanation and help! I adapted the code you used in your project and it works for me now.

    Two minor (aesthetic) details: Ideally, I’d like to have the “age” error message to be displayed on top of the other ones, given that that’s the first piece of information that’s required. Not a big deal, but I was wondering whether / how that could be implemented (if it’s not too much of an effort).

    And secondly, is there a way to add some space with the before code? On my screen, the TextInput box and DropDown menus are pretty much glued to the text that precedes them…

    Thanks again!
    Kathy

    #6757
    Jeremy
    Keymaster

    Hi Kathy,

    1. This is due to how complex conditionals (and/or) are processed: the parent (first) one actually takes effect last. So make the “job” test the parent (first) one, and make the “age” test its first child and test, this way the “job” error message will be displayed at the bottom and the “age” error message will be displayed at the top

    2. Simply add a margin-right CSS rule to your Text element. Given the content of your trial, you can even make it part of the default css command

    I have updated the project at https://farm.pcibex.net/r/SsqEZD/ along those lines

    Jeremy

    #6759
    kathy
    Participant

    Thank you so much!!
    – Kathy

    #6774
    kathy
    Participant

    Hi Jeremy,

    sorry to bother you again – I added a few options (a drop-down and a text input) to this page and now I get error messages for “job” and “country” (TextInput) even if correctly filled out, which prevent me from continuing. I’m guessing I’ll have to rearrange the order of the code, but I’m not sure how:

     newButton("okay", "Weiter")
            .print()
            .wait(
                // job
                getDropDown("job").test.selected()
                    .failure( newText('errorjob', "Bitte gib Deine Hauptbeschäftigung an.").color("red").print() )
                // age
                .and( getTextInput("age").test.text(/^\d+$/)
                        .failure( newText('errorage', "Bitte gib Dein Alter an.").color("red").print() )
                // sex
                ).and( getDropDown("sex").test.selected()
                        .failure( newText('errorsex', "Bitte gib Dein Geschlecht an.").color("red").print() )
                // mother tongue
                ).and( getDropDown("german").test.selected()
                        .failure( newText('errorgerman', "Bitte gib an, ob Deutsch Deine Muttersprache ist.").color("red").print() )
                // bilingualism
                ).and( getDropDown("bilingual").test.selected()
                        .failure( newText('errorbilingual', "Bitte gib an, ob Du bilingual aufgewachsen bist.").color("red").print() )
                // nationality
                ).and( getDropDown("nationality").test.selected()
                        .failure( newText('errornationality', "Bitte gib Deine Nationalität an.").color("red").print() )
                // education
                ).and( getDropDown("bildung") .test.selected()
                        .failure( newText('errorbildung', "Bitte gib Deinen Bildungsstand an.").color("red").print() )
                 // country
                ).and( getTextInput("country").test.text()
                        .failure( newText('errorcountry', "Bitte gib an, in welchem Land Du in letzten 5 Jahren gewohnt hast.").color("red").print() )
                )
            )      
    

    Any chance you could have another look? Thanks a lot!

    Best,
    Kathy

    #6775
    Jeremy
    Keymaster

    Hi Kathy,

    This is because you’re testing that the content of your “country” TextInput should be empty: whenever you click the button and there is at least one character in that input box, your last test will fail and accordingly print the ‘errorcountry’ Text element. Because all the ands are attached to the first test on “job,” whenever one test fails it systematically triggers failure for that first one too (the “parent”), hence the printing of the “errorjob” Text element.

    So the solution is to make your test on ‘country’ check that there’s at least one character in the input box (see this message above) and, if you want to make each test’s failure independent from one another, you can insert a dummy test as the parent and make all the other ones children of that test:

    newFunction('dummy', ()=>true).test.is(true)
    // age
    .and( getTextInput("age").test.text(/^\d+$/)
    // ...

    (Note that you no longer need to move the last test up to the top of the stack in order for the error messages to show in the correct order, now that we’re using a dummy test as the parent)

    I have updated the code at the project whose demonstration link I shared in my previous post

    Jeremy

    #6779
    kathy
    Participant

    Thank you!!

    #6780
    Anna
    Participant

    Hi Jeremy and Kathy,

    just a heads up about your code: if a participant enters a string instead of a number in the age input field (e.g. “AP” instead of “99”), they are shown the error message but they cannot correct their input to a number (or otherwise).

    Best,
    Anna

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