Record keypress and response time without advancing - javascript

I'm very new to JavaScript. I have a Qualtrics survey and I'm trying to create a question to which respondents must first press the spacebar, and then respond to a math problem on the screen. What I want is for Qualtrics to record (1) whether they press the spacebar (score as "C") or not (score as "X"), (2) if they do, how long it took them to press the spacebar from when the page appeared, and (3) to NOT advance to the next screen on the keypress, since they still have to answer the math problem on the screen. Here's what I've been trying:
Body of the Qualtrics question Q1:
Please press the spacebar, then solve this problem: 1+12+15-13-3+19-9=?
JavaScript associated with Qualtrics question Q1:
Qualtrics.SurveyEngine.addOnload(function()
{
var day = new Date();
trialstart = day.getTime();
$(document).on('keydown', function(e) {
if(e.keyCode === 32) {
var day = new Date();
trialend = day.getTime();
rt = trialend - trialstart;
document.getElementById("Q1").value = document.getElementById("Q1").value + "C" + rt + ",";
}
else{
document.getElementById("Q1").value = document.getElementById("Q1").value + "X" + ",";
}
});
});
Right now, this script isn't doing anything; Qualtrics outputs whatever is typed into the response textbox associated with Q1 (for responding to the math problem), but doesn't record whether they pressed the spacebar or their response time for pressing that key. Any help would be greatly appreciated. Thanks.

It is likely that you are seeing no output from this because you're using "Q1" as the ID. Qualtrics element ID's are actually a bit different from that, so the best way to find them is to preview your survey and inspect them using your browser to find the proper ID.
Now for a more full answer, your Javascript has no way to unbind the keypress event. So it will always end up as X, not C, unless they end their text entry with a space. Instead try something like this:
Qualtrics.SurveyEngine.addOnload(function(){
var pageStart = new Date();
var trialstart = pageStart.getTime();
var that = this;
that.hideNextButton();
var para = document.createElement("footnote");
var test = document.getElementById("Buttons");
var node = document.createElement('input');
var next = document.getElementById("NextButton");
var rt;
node.id = "newButton";
node.type = "button";
node.name = "newButton";
node.value = " Continue ";
var fired = 0;
var spaceFirst = '';
$(document).on('keydown', function(e) {
if(fired != 1){
if(e.keyCode === 32) {
var day = new Date();
var trialend = day.getTime();
rt = trialend - trialstart;
spaceFirst = 'C';
fired = 1;
}
else{
spaceFirst = 'X';
fired = 1;
}
}else{}
});
node.onclick = function stuff(){
if(spaceFirst == 'C'){
document.getElementById("QR~QID1").value = document.getElementById("QR~QID1").value + ', ' + spaceFirst + ", " + rt;
}else if(spaceFirst == 'X'){
document.getElementById("QR~QID1").value = document.getElementById("QR~QID1").value + ', ' + spaceFirst;
}else{}
window.alert(document.getElementById("QR~QID1").value);
that.clickNextButton();
};
para.appendChild(node);
test.insertBefore(para, next);
});
What this does is, hides the next button from view and creates a new button in it's place. (You will need to style this button, id = "newButton", to match your current button). Then it listens for the first keydown event, if it is a space, then it sets a variable equal to C, otherwise it sets it equal to X. Then it disables the keydown listener so that it doesn't fire after every keypress.
When the new button is pressed, it adds the appropriate X or C and the page time. You can remove or comment out the alert, as it is just for testing/debugging purposes.

Related

Javscript listen keyboard, on text input

I want to show popup if user input text no matter where on website, I see that javascript have key listener but what is the best way if I want to show popup when user write "ShowMePopup"?
I have write this script for test
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
var name = event.key;
var code = event.code;
console.log(`Key pressed ${name} \r\n Key code value: ${code}`);
switch (event.key) {
case "ArrowDown":
alert('arrow down')
break;
case "Shift + S + h + o + w + Shift + M + e + Shift + P + o + p + u + p":
alert('Secret Popup')
break;
default:
return;
}
event.preventDefault();
}, true);
I have try to add case with
case "Shift + S + h + o + w + Shift + M + e + Shift + P + o + p + u + p":
but it wont work, any ideas?
When a key is pressed, push it into an array. Then, check the last 11 items (the length of the desired key combo) in the array for your key combination:
const keysPressed = [];
const popupKeyCombo = ['Shift', 'S', 'h', 'o', 'w', 'Shift', 'M', 'e', 'Shift', 'P', 'o', 'p', 'u', 'p'];
window.addEventListener("keydown", e => {
keysPressed.push(e.key);
if(keysPressed.slice(-popupKeyCombo.length).join() === popupKeyCombo.join()){
alert('Secret Popup');
}
});
And consider adding additional logic to keep the array at a reasonable size.
You can solve the issue by creating a storage variable outside of your listener function and store each key event inside of it. However, be careful with the timing of keypresses since holding shift longer, multiple key events will be fired. But with this you should make it work.
let storage = "";
window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
storage += event.key;
console.log(storage);
if(storage.includes("ShiftShowShiftMeShiftPopup")){
alert("Surprise");
storage = "";
}
event.preventDefault();
}, true);
You may keep track of what was typed in each occurence of the keydown event and then do a substraction between the expected text and what was captured so far.
As soon as the capture diverges from what's expected, the captured string will be reset.
At each character typed, the demo will print on console the full string expected, what was captured so far since the latest reset and what's still missing.
When the passphares will be catched correctly, the function secretUnlocked gets triggered.
This solution won't have a infinitely growing buffer but just the captured string as long as it was matching the expected input. As soon as the typed string diverges, the buffer gets reset and the minimum amount of memory gets used.
let captured = "";
let expected = "ShowMePopup";
//substracts captured to expected and returns the text fragment still missing
function diff(expected, capture){
const split = expected.split(capture);
if(split[0].length > 0 || split.length > 2)
return expected;
else
return split[1];
}
document.addEventListener('keydown', function(event) {
//character allowed (this is a lazy trick to exclude special keys like shift, ctrl...)
const allowedChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
if(!allowedChars.split('').includes(event.key)){
return;
}
//appends the latest typed char to the captured string
captured += event.key;
const stillExpecting = diff(expected, captured);
//if stillExpecting is still the same as expected, resets the captured part
if(stillExpecting === expected)
captured = '';
console.log(`expecting: ${expected} .. so far: ${captured} .. still expecting: ${stillExpecting}`);
//if stillExpecting has zero length, the condition is met
if (stillExpecting.length === 0){
secretUnlocked();
}
});
function secretUnlocked(){
console.log('Secret Unlocked');
}
Type the secret passphrase while the document has focus...
Wow, what a lot of code.
This one works too
let text = "";
const phrase = "ShowMePopup";
window.addEventListener("keydown", e => {
const key = event.key;
if (key.length !==1) return; // ignore shift etc. The shifted char is in the next key
text += key;
if (text.indexOf(phrase) != -1) {
console.log("Show popup");
text = ""; // to handle ShowMePopupShow....
}
else if (phrase.indexOf(key) === -1) text = ""; //reset
});
You mean a key.detector with a popup like this ?. Try this code.
function showKC(event) {
var x = event.keyCode;
var y = event.key;
document.getElementById("charcode").innerHTML = "Key: "+y+" Code: "+x;
}
function CheckPop() {
var smp = "ShowMePopup";
var text = document.getElementById("t1");
if (text.value == smp) {
alert(smp);
}
}
Just type on keyboard <input type="text" id="t1" value="" onkeyup="showKC(event), CheckPop()"/>
<div id="charcode"></div>

Id of an created element can't be used

My problem is:
I created an element:
var crt = document.createElement("BUTTON");
var lst = document.createTextNode("\u2103");
crt.appendChild(lst);
crt.id = "change";
$(".information").append(crt); // information is a div
When it is clicked, temperature is changing from Celsius to Fahrenheit and back:
var find = true;
var c;
var d = document.getElementById("change");
$( "#change" ).click(function() {
if(find === true) {
c = Math.trunc(s * 1.8 + 32); // s is the temperature in celsius from start
t.innerHTML = "<br>Temperature: " + c;
d.innerHTML = '℉'
find = false;
} else {
c = Math.trunc((c - 32) / 1.8);
t.innerHTML = "<br>Temperature: " + c;
d.innerHTML = "℃";
find = true;
}
});
If i try to change something to id "change" on CSS, everything works fine.. But when i use it in script doesn't work.. But if the element with id "change" is created manually from the start on document, the code works.. Can you guys tell me what is the problem ? Why using it like this doesn't work ?
Thanks in advance.
The code on lines 49-67 in the supplied codepen execute and attempt to set a click handler on the element with the id of change. The element is not yet in the document when the code runs.
You will need to call that code after you dynamically create the element with the id of change.

How Facebook changes the Emoji from their symbols while chatting?

First Image - Initial Text when typing something in Facebook Chat Box
Second Image - The moment you hit space it converts to this!
I have seen in developer console it is not the input box at all, they are using span and all with background-image to do it but how to actually combine it completely, to avoid any clutter whatsoever. I am attaching a link of codepen of what I did when pressing Enter key. But not able to do for the Space Bar. Codepen Link Anything you guys can help. Thanks in advance. NOTE :- No external libraries and would prefer Javascript answer.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keypress", function (e) {
console.log("Inside keypress event")
var textEntered = chatDetailText.value;
var keyPressed = e.which || e.keyCode;
console.log(emojiContainer[this.value])
if (keyPressed === 13){
console.log("inside keypress 13")
if(emojiContainer[this.value]){
console.log("inside value found of enter")
var emojiImg = document.createElement("img");
emojiImg.src = emojiContainer[this.value];
document.getElementById('enterPressed').appendChild(emojiImg);
document.getElementById('chatDetailText').value = '';
}else{
console.log("value not found of enter")
var divChatDetail = document.createElement('div');
/*chatDetailSeperator.className = "aClassName";*/ //To add class name for div
divChatDetail.innerHTML = this.value;
document.getElementById('enterPressed').appendChild(divChatDetail);
document.getElementById('chatDetailText').value = '';
}
}
}, false);
You can use HTML5 ContentEditable attribute for div.
here is just an example. Take care of cert position etc.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keypress", function (e) {
console.log("Inside keypress event")
var textEntered = chatDetailText.innerHTML;
var keyPressed = e.which || e.keyCode;
console.log(keyPressed)
if (keyPressed === 32){
var last_word = textEntered.split(" ");
last_word = last_word[last_word.length-1];
console.log(last_word);
if(emojiContainer[last_word]){
console.log("inside value found of enter")
var emojiImg = "<img src='"+emojiContainer[last_word]+"' >";
textEntered = textEntered.replace(last_word, emojiImg)
chatDetailText.innerHTML = textEntered;
}
}
}, false);
<div id="enterPressed"></div>
<div contenteditable="true" id="chatDetailText" >edit this</div>
I got it done, thanks to Zeeshan for helping me with contenteditable. Do update if you have any improvisations.
var emojiContainer = {
":)" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zeb/2/16/1f642.png",
"<3" : "https://static.xx.fbcdn.net/images/emoji.php/v9/zed/2/16/2764.png"
};
var chatDetailText = document.getElementById('chatDetailText');
chatDetailText.addEventListener("keydown", function (e) {
//to perform the action based on pressing space bar (32) or enter (13).
var keydown = e.which || e.keyCode;
//to get the pointer location and modify to place to the end if needed
var selectionInfo = getSelectionTextInfo(this);
//to get the complete text extered by the user.
var input = chatDetailText.innerHTML;
//to cover the cases in which user enters <3 and gets interpreted as &lt
var textEntered = decodeHtml(input);
//To split the text entered and to get the location of the emoji for conversion
var last_word = textEntered.split(/\s{1}/);
//After splitting contains the emoji and now can be accessed.
last_word = last_word[last_word.length-1];
//space bar is pressed and the smiley is just inserted
if (keydown === 32 && selectionInfo.atEnd){
//if the emoji is available in our database, it'll replace the same using the Facebook url which is currently used.
if(emojiContainer[last_word]){
var emojiImg = "<img src='"+emojiContainer[last_word]+"' >";
textEntered = textEntered.replace(last_word, emojiImg);
chatDetailText.innerHTML = textEntered;
elemIterate = document.getElementById('chatDetailText');//This is the element to move the caret to the end of
setEndOfContenteditable(elemIterate);
}
//Enter key is pressed after typing the emoji
}else if (keydown === 13) {
// To avoid extra line insertion in div.
e.preventDefault();
//if the emoji is available in our database, it'll replace the same using the Facebook url which is currently used.
if(emojiContainer[last_word]){
var emojiImg = document.createElement("img");
emojiImg.src = emojiContainer[last_word];
var spanChatElement = document.createElement("span");
var precedingChatContent = textEntered.split(/\s{1}/);
precedingChatContent.pop(); //To pop the last smiley found
if(precedingChatContent.length !=0){
precedingChatContent = precedingChatContent.join(" ");
spanChatElement.innerHTML = precedingChatContent;
document.getElementById('enterPressed').appendChild(spanChatElement);
}
document.getElementById('enterPressed').appendChild(emojiImg);
document.getElementById('chatDetailText').innerHTML = '';
}else{
//If no Smiley found, just the plain text it'll automatically display the text in a div
var divChatElement = document.createElement('div');
//chatDetailSeperator.className = "aClassName"; To add class name for div
divChatElement.innerHTML = textEntered;
document.getElementById('enterPressed').appendChild(divChatElement);
document.getElementById('chatDetailText').innerHTML = '';
}
}
}, false);
function decodeHtml(html) {
var textAreaElement = document.createElement("textarea");
textAreaElement.innerHTML = html;
return textAreaElement.value;
}
//To send the pointer to the end of the div.
function setEndOfContenteditable(contentEditableElement){
var range,selection;
if(document.createRange)//Firefox, Chrome, Opera, Safari, IE 9+
{
range = document.createRange();//Create a range (a range is like the selection but invisible)
range.selectNodeContents(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
}
else if(document.selection)//IE 8 and lower
{
range = document.body.createTextRange();//Create a range (a range is like the selection but invisible)
range.moveToElementText(contentEditableElement);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
//To check if it is at the end.
function getSelectionTextInfo(contentEditableElement) {
var atEnd = false;
var selectionRange, testRange;
if (window.getSelection) {
var windowSelection = window.getSelection();
if (windowSelection.rangeCount) {
selectionRange = windowSelection.getRangeAt(0);
testRange = selectionRange.cloneRange();
testRange.selectNodeContents(contentEditableElement);
testRange.setStart(selectionRange.endContainer, selectionRange.endOffset);
atEnd = (testRange.toString() == "");
}
}else if (document.selection && document.selection.type != "Control") {
selectionRange = document.selection.createRange();
testRange = selectionRange.duplicate();
testRange.moveToElementText(contentEditableElement);
testRange.setEndPoint("StartToEnd", selectionRange);
atEnd = (testRange.text == "");
}
return { atEnd: atEnd };
}
You just need to change the line if (keyPressed === 13){ to if (keyPressed === 32){ in your codepen link. And to stop that from posting the comment, you just need to add another function for if (keypressed === 13).

Reddirecting to another page with created button

I have this piece of code. If conditions are met i create a button "Show on Map". Now I need to run onclick event on created button that reddirects the user to a map on new URL. I tried with "window.location" in function go_to_map but it doesn't work. Any help?
function coordinates_Conv (d, m, s) {
return d + m / 60 + s / 3600;
}
function alarm(){
var lat_d = parseInt(document.getElementsByClassName("lat_deg")[0].value);
var lat_m = parseInt(document.getElementsByClassName("lat_min")[0].value);
var lat_s = parseInt(document.getElementsByClassName("lat_sec")[0].value);
var lon_d = parseInt(document.getElementsByClassName("lon_deg")[0].value);
var lon_m = parseInt(document.getElementsByClassName("lon_min")[0].value);
var lon_s = parseInt(document.getElementsByClassName("lon_sec")[0].value);
if ((coordinates_Conv (lat_d,lat_m,lat_s)<=90) && (coordinates_Conv (lat_d,lat_m,lat_s)>=0) && (coordinates_Conv (lon_d, lon_m, lon_s)<=180) && (coordinates_Conv (lon_d, lon_m, lon_s)>=0)){
document.getElementById("vypocet").innerHTML= String(Math.round(coordinates_Conv (lat_d,lat_m,lat_s)*1000)/1000) + "<br>" + String(Math.round(coordinates_Conv (lon_d, lon_m, lon_s)*1000)/1000);
var show_map = document.createElement("button","map");
show_map.setAttribute("id","map");
show_map.setAttribute("onclick", go_to_map);
show_map.innerHTML = "Show on map";
document.body.appendChild(show_map);
}
else {
alert("Invalid input");
}
}
function go_to_map(){
var targetMap = "https://www.mapurl.com/"
window.location=targetMap;
}
Your onclick handler never gets called in the first place.
To set it handler correctly, set the element's onclick directly:
show_map.onclick = go_to_map;
You need to replace show_map.setAttribute("onclick", go_to_map); with show_map.onclick = go_to_map;.
This is because onclick is an event and not an attribute.

How to force loop to wait until user press submit button?

I have simple function which checks if entered pin code is valid. But i don't know how to force for-loop to wait until i enter code again to check again it's validity.
So how it should be - i type PIN code, then click OK button and it checks whether it's correct (if it is, i can see my account menu; if it's not i have to type it again and i have 2 chances left). My code fails, because PIN when code is wrong program should wait until i type new code and press OK button again.
I tried setTimeout(), callback(), but it doesn't work. This is what i have - a function with for-loop that just runs 3 times (as it is suppose to, but not instantly) without giving a chance to correct the PIN code.
That's whole, unfinished yet, code: http://jsfiddle.net/j1yz0zuj/
Only function with for-loop, which checks validity of PIN code:
var submitKey = function(callback)
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
for (i=0; i<3; i++)
{
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (2-i);
callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
if (i=2) console.log("blokada");
}
}
else if (digitStatus == 1)
{
}
}
Your approach is wrong. You should not make the user wait!!! You need 2 more variables at the top of your programm pincount=0 and pininputallowed. Increase pincount in the submit key function by 1 and then check if pincount<3.
Here is a corrected version of your code.
http://jsfiddle.net/kvsx0kkx/16/
var pinCount=0,
pinAllowed=true;
var submitKey = function()
{
console.log("digit status" + digitStatus);
if (digitStatus == 0)
{
correctPIN = 1234;
var onScreen = document.getElementById("screen");
pinCount++;
if(pinCount >= 3) {
pinAllowed = false;
onScreen.innerHTML = "<br>blokada";
}
if(pinAllowed){
if (onScreen.innerHTML.slice(15, onScreen.innerHTML.length) == correctPIN)
{
setTimeout(accountMenu, 1250);
//break;
}
else
{
onScreen.innerHTML += "<br> Błędny kod PIN! Wpisz PIN ponownie. <br> Pozostało prób: " + (3-pinCount);
inputLength = 0;
document.getElementById("screen").innerHTML += "<br>Wpisz kod PIN: ";
//callback();
//cardInserted = function(function(){console.log("Ponowne wpisanie PINu");});
}
}
}
else if (digitStatus == 1)
{
}
}
You need to create much more variables to control your machine. Your add/delete digit function had conditions that were badly written and only worked if the text on the screen was short enough.
var inputLength = 0;
addDigit = function(digit){
//numKeyValue = numKeyValue instanceof MouseEvent ? this.value : numKeyValue;{
if (inputLength < pinLength) {
onScreen.innerHTML += this.value;
inputLength++;
}
//if (onScreen.innerHTML == 1234) console.log("PIN został wprowadzony");
},
delDigit = function(){
if (inputLength >= 0) {
onScreen.innerHTML = onScreen.innerHTML.slice(0, -1);
inputLength--;
}
};
If you want to empty the screen at any moment you can insert onScreen.innerHTML = ''; anywhere
ps: Thanks for the exercise and nice automat you made there.

Categories