4

I'm programming an experiment in Qualtrics and I basically need to create, with Javascript, a variable that tells me how long (total) participants held down any button. Pressing a button will display text to the participants and releasing it will make the text disappear, so basically it tells me how much time they had to read the text. So let's say they press the button three times, first for 30 seconds, second for 10 seconds and third for 2 seconds, this code should store the value 42.

What I have so far is this:

addEventListener("keydown", function(event) {
if (event.keyCode == 86)
    document.getElementById("text").innerHTML = "Text to show";
    var d1 = new Date();
    document.getElementById("div1").innerHTML = d1.getTime(); 

});
addEventListener("keyup", function(event) {
if (event.keyCode == 86)
    document.getElementById("text").innerHTML = "";
    var d2 = new Data();
    var d1 = parseFloat(document.getElementById("div1"));
    var diff = d2 - d1.getTime();
    var old = parseFloat(document.getElementById("div2"));
    var old = old + diff;
    document.getElementById("div2").innerHTML = old;        
    Qualtrics.SurveyEngine.setEmbeddedData("readingtime", totalTime);
});

I store the values in divs because I can't seem to reuse values from one event listener to the other (this is probably because I don't know enough about javascript and scopes). Oh, and the last function is just a Qualtrics-specific function to store the value in the database. Anyway, I can't get it to work, when I check the variable in the database it is simply empty. Anyone can spot what I'm doing wrong?

5 Answers5

3

I did a few changes to your code:

  • Added global variables
  • Added few missing brackets
  • Attached listeners to window
  • Removed multiple calls to DOM elements
  • Created a function for each event listener
  • Rounded the elapsed time to seconds

var d0;
var d1;

var subtotal = 0;
var total = 0;

var div1 = document.getElementById("div1");
var text = document.getElementById("text");

window.addEventListener("keydown", dealWithKeyDown, false);
window.addEventListener("keyup", dealWithKeyUp, false);

function dealWithKeyDown(event) {
    if (event.keyCode == 86) {

        if (typeof d0 === 'undefined') {
            d0 = new Date();
        }

        d1 = new Date();        
        subtotal = Math.round((d1.getTime() - d0.getTime()) / 1000);  

        div1.innerHTML = subtotal;
        text.innerHTML = "Text to show";
    }
}

function dealWithKeyUp(event) {
    if (event.keyCode == 86) {
        total = total + subtotal;
        text.innerHTML = "";
        d0 = undefined;       
        Qualtrics.SurveyEngine.setEmbeddedData("readingtime", total);
    }
}
R.Costa
  • 1,395
  • 9
  • 16
2

Okey dokey, since none of the posted answers seem to have been accepted, I'm gonna post my own.

There's really not that much to say about this solution, it's as easy as it gets, nicely put into objects, so that we know what's going on, and I am even giving you a fiddle for it!

There's one problem I don't know if I solved or not, but sometimes the button will show that it has been pressed for millions of seconds, I think that's because Key is not being initialized properly, which is pretty weird, but it happens rarely enough for me to put the burden of fixing it on you.

The code is here: https://jsfiddle.net/vo2n1jw1/

Pasted over:

var Key = function(code)
{
    this.code = code;
};

Key.prototype.time = 0;
Key.prototype.pressedAt = 0;

Key.prototype.getTimeInSeconds = function()
{
    return this.time / 1000;
};

var Keyboard = function()
{
    this.keys = [];
};

Keyboard.prototype.addOrGetKey = function(code)
{
    var key = this.getKey(code);

    if(!key)
    {
        key = new Key(code);

        this.addKey(key);
    }

    return key;
};

Keyboard.prototype.addKey = function(key)
{
    this.getKeys()[key.code] = key;
};

Keyboard.prototype.getKey = function(code)
{
    return this.getKeys()[code];
};

Keyboard.prototype.getKeys = function()
{
    return this.keys;
};

Keyboard.prototype.printAllKeysIntoElement = function(element)
{
    var keys = this.getKeys();
    var length = keys.length;

    element.innerHTML = "";

    for(var i = 0; i < length; i++)
    {
        var key = keys[i];

        if(!key)
        {
            continue;
        }

        var keyElement = document.createElement("div");
        keyElement.innerHTML = "Button: " + key.code + " has been pressed for " + key.getTimeInSeconds() + " seconds";

        element.appendChild(keyElement);
    }
};

var KeyboardListener = function(keyboard, element)
{
    this.keyboard = keyboard;
    this.container = element;

    this.onKeyDownThis = this.onKeyDown.bind(this);

    document.addEventListener("keydown", this.onKeyDownThis, false);
    document.addEventListener("keyup", this.onKeyUp.bind(this), false);
};

KeyboardListener.prototype.onKeyDown = function(event)
{
    console.log("press");

    var keyboard = this.getKeyboard();
    var code = event.keyCode;
    var key = keyboard.addOrGetKey(code);

    key.pressedAt = Date.now();

    document.removeEventListener("keydown", this.onKeyDownThis, false);

    return false;
};

KeyboardListener.prototype.onKeyUp = function(event)
{
    console.log("release");

    var keyboard = this.getKeyboard();
    var code = event.keyCode;
    var key = keyboard.addOrGetKey(code);

    if(key.pressedAt)
    {
        key.time += Date.now() - key.pressedAt;

        keyboard.printAllKeysIntoElement(this.container);
    }

    document.addEventListener("keydown", this.onKeyDownThis, false);

    return false;
};

KeyboardListener.prototype.getKeyboard = function()
{
    return this.keyboard;
};

var resultsElement = document.getElementById("results");

var keyboard = new Keyboard();
var listener = new KeyboardListener(keyboard, resultsElement);

There's 3 objects:

Key Keyboard KeyboardListener

They really do what they sound like.

Tell me, if you want anything explained.

Oh, one thing, I know you're not supposed to use arrays like this, but I was lazy.

0

You can try to create a variable outside both listeners, and outside functions, so you can use it anywhere. And I think you are doing well by getting the times of Dates.

var firstTime = new Date();
var totalTime = 0;

addEventListener("keydown", function(event) {
if (event.keyCode == 86) {
    document.getElementById("text").innerHTML = "Text to show";
    firstTime = new Date();
}
});
addEventListener("keyup", function(event) {
if (event.keyCode == 86) {
    document.getElementById("text").innerHTML = "";
    var d2 = new Date();
    var diff = d2.getTime() - firstTime.getTime();
    totalTime += diff;

    Qualtrics.SurveyEngine.setEmbeddedData("readingtime", totalTime);
}
});
Khalav
  • 77
  • 7
  • This seems to work as well, thanks! But somehow it returns seconds and not milliseconds, which would be preferable in my case (I am measuring participant behavior, so more precision is better). – Cristina Mendonça Jan 31 '16 at 10:59
  • @Khalav `keydown` listener gets called repeatedly when the key is holded. With this, this code doesn't work. you can test it. – Mehran Torki Feb 01 '16 at 09:53
0

It's better to declare d1 & d2 variables global instead of using html elements as storage.

var d1, d2;
var total_time = 0;
var key_pressed = false;

addEventListener("keydown", function(event) {
    if (event.keyCode == 86) {
        if (!key_pressed)
           d1 = new Date();
        key_pressed = true;
    }   
});

addEventListener("keyup", function(event) {
    if (event.keyCode == 86) {
        key_pressed = false;
        d2 = new Date();
        total_time += d2.getTime() - d1.getTime();
        Qualtrics.SurveyEngine.setEmbeddedData("readingtime", total_time);   
    }
});

When the 'v' key is pressed and hold, keydown listener is called repeatedly. That's why there is boolean variable i.e. "key_pressed" to detect hold key first time.

Mehran Torki
  • 977
  • 1
  • 9
  • 37
-1

Where does the "totalTime" get assigned?

Is your totalTime has any value? Did you try to debug the javascript using the browser console?

If you don't want to store data to divs, u can store it within global vars.

var times = {
    start: 0,
    total: 0
}

addEventListener("keydown", function(event) {
    if (event.keyCode == 86) { 
        // i think your hole code should be executed only on releasing this one button
        document.getElementById("text").innerHTML = "Text to show";
        times.start = new Date();
    }
});

addEventListener("keyup", function(event) {
    if (event.keyCode == 86) {
        // i think your hole code should be executed only on releasing this one button
        document.getElementById("text").innerHTML = "";
        var diff = new Date().getTime() - times.start.getTime();
        times.total = times.total + diff;
        Qualtrics.SurveyEngine.setEmbeddedData("readingtime", times.total);
    }
});

So you can use the var times outside of the event listener function to declare it global.

I noticed that you used the if ().. without { and }, so only the next line is affected, im dont shure if you want that.

Lukas Bonzelett
  • 308
  • 2
  • 13
  • Please don't use the *Post answer* button for questions to the original poster. With a bit more rep, [you will be able to post comments](http://stackoverflow.com/privileges/comment). – Heretic Monkey Jan 27 '16 at 15:17
  • I only can comment my own post until i got 50 reputaions, so im not able to comment the answer before. – Lukas Bonzelett Jan 27 '16 at 15:23
  • Right. Like I the link in my previous comment notes. What I'm saying is, don't break the rules to use answers for comments. Use answers for answers. Work on getting your rep up, then use comments for comments. [Check out this Meta post for the reasoning](http://meta.stackoverflow.com/questions/252133/50-reputation-points-to-make-comments) – Heretic Monkey Jan 27 '16 at 15:59
  • @LukasBonzelett `keydown' listener gets called repeatedly when the key is holded thus start time is not correct. So this code doesn't work. – Mehran Torki Feb 01 '16 at 09:43