Individual custom start position in Qualtrics through Javascript - javascript

I want to use either a slider question or a draggable bar chart in Qualtrics to present to respondents how they answered in former questions. More specifically, I compute a value out of then answers with weighting, and want the slider or bar to be positioned at this value.
Notably, as each respondent has a value (stored in an embedded data field), the position will thereby be individual for each respondent. Piping only works for text fields, as far as I understood the support page.
Based on this question/answer I came to the following code for the bar graph:
Qualtrics.SurveyEngine.addOnload(function()
{
var result = "${q://Field/result}";
var qwidth = $('QID1936~1~track').offsetWidth;
var resrec = ((qwidth*result)/100);
$('QID1936').select('.bar').each(function(name, index) {
name.setStyle({ width: resrec +"px"});
});
});
Basically, I get the result for each respondent out of the embedded data, get the width of the full bar graph, compute the ratio that should be colored based on the result, and update the position of the bar graph (following the mentioned answer).
Funny enough, everything works when done in the console. Also, the embedded data is correctly loaded, qwidth as well.
Two problems arise: it seems resrec could be computed wrongly, as a console.log() spits out 0 instead of the correct value. I assumed this could be somehow as a variable is not recognized as number, but several tries with Number() or 0+var did not change how this works in Qualtrics. In the console, it works just fine.
Also, no update of the bar (or slider with similar code) happens, neither with the correct value nor with the 0 that is produced by Qualtrics.
I search for two things: either a solution to the Javascript problem as described, basically how I can update the bar or slider with embedded data. Or another solution how to directly get embedded data into one of those two question formats as a starting value for each respondent individually.
Thanks for your help or ideas!

Try this:
Qualtrics.SurveyEngine.addOnload(function()
{
var qid = this.questionId;
var result = parseFloat("${e://Field/result}");
var qwidth = $(qid+'~1~track').offsetWidth;
var resrec = ((qwidth*result)/100);
$(qid).select('.bar').each(function(name, index) {
name.style.width = resrec + "px";
});
});
Notes:
It is best not to use a hardcoded QID
In a pipe use e: to refer to an embedded variable. q: is for questions.
Use parseFloat to convert the string to a number
No need to use setStyle if you are only setting one value

One solution proposed by Qualtrics support: when you use bars and/or sliders, piped values are actually possible.
The trick is to have the value of the bar/slider shown (a thing we do not use in the whole survey elsewhere). Then, you can access over the Advanced Question Options > Add Default Choices the blue arrow for piping text behind the value. Through this, the value is individually set to either embedded data or another answer.
Note, however, to tick "Show value" before accessing the default choices, else you will only be able to drag around the bar and set it for all simultaneously.

Here is a solution using the Qualtrics Question API method setChoiceValue that does not require you to compute the ratio and update the length of the bars manually.
Below is an example code for the result of ten respondents saved in embedded data from previous questions.
Qualtrics.SurveyEngine.addOnload(function()
{
var embedded = ["${e://Field/r1}", "${e://Field/r2}",
"${e://Field/r3}", "${e://Field/r4}", "${e://Field/r5}",
"${e://Field/r6}", "${e://Field/r7}", "${e://Field/r8}",
"${e://Field/r9}", "${e://Field/r10}"];
for (var i = 0; i < 11; i++) {
var index = i + 1;
var choiceInput = embedded[i];
this.setChoiceValue(index, choiceInput);
}
});
For one respondent:
Qualtrics.SurveyEngine.addOnload(function()
{
var value = "${e://Field/r1}";
this.setChoiceValue(1, value);
});

Related

Create a Range spanning the character before cursor in Word Javascript API / Office.js

I'm trying to create an Office Javascript Add-in which will examine the character before the cursor and replace that character depending on what it is. So I need to create a Range of the character before the cursor. I can do this easily with a VBA macro, but unfortunately, I can't find a way to do this with the new javascript api. Is this possible?
If this is possible, it would also be helpful if I could look at the 5 characters before and after the cursor for added context.
Thanks.
A couple months ago I tried something similar. In short there is no good way to do it. You could try what I will specify below, but I would advice against it. The example is not thought through and most likely will contain a number of bugs. Additionally I find this an incredibly inefficient way to do something so simple.
Limitations in the API that prevent an easy solution:
There is no cursor, only selections. This means that you need to make an assumption that the cursor is always at the beginning of a selection.
Selections cannot be directly modified through the Office.js API. So it is not possible to expand the selection to include the previous character.
The 'Range' object does allow to be extended into both directions, but it requires another range as input. This means an earlier range needs to created/found (i.e. a range object before the current selection).
You can only navigate outside of the selection through the property 'parentBody' which will give you the entire body of the document. This needs to be processed in order to isolate a range before the cursor that could help us replace the character.
From what I can tell it is not possible to create a range for a single character. So a bigger range needs to be taken before the cursor and needs to replaced entirely.
Example
// WARNING: Incredibly inefficient and poor code. Do not use directly!
// WARNING: Edge cases are not tackled in this example.
function replaceCharacterBeforeCursor() {
Word.run(function (context) {
var selection = context.document.getSelection();
// Assumption: Cursor always starts at the beginning of a selection.
var cursor = selection.getRange('Start');
// Create a new range that covers everything before the cursor (or the beginning of the selection).
var startDocument = selection.parentBody.getRange("Start");
var rangeBeforeSelection = startDocument.expandTo(startDocument);
// Capture parent paragraph.
var parentParagraph = rangeBeforeSelection.paragraphs.getLast();
context.load(parentParagraph);
context
.sync()
.then(function () {
// Create range that captures everything from the beginning of the parent
// paragraph until the cursor.
var paragraphStart = parentParagraph.getRange('Start');
var wordRangeBeforeCursor = paragraphStart.expandTo(cursor);
context.load(wordRangeBeforeCursor);
context
.sync()
.then(function () {
// Replace last character.
var oldText = wordRangeBeforeCursor.text;
var wordLength = oldText.length;
var lastCharacter = oldText.substring(wordLength - 1);
if (lastCharacter !== " ") {
var newText = oldText.substring(0, wordLength - 1) + "test";
wordRangeBeforeCursor.insertText(newText, 'Replace');
context.sync();
}
});
});
});
}
Another way to do it is through text ranges. This would be substantially more inefficient. Either way I hope this will help you in finding a solution that fits your needs.

onkeydown, and auto complete

I was wondering if anyone could help me solve this issue or point me towards the right direction.
In my project we have a filed that needs to be autofilled, at this moment I use onblur which works wonders as it only does it so once you leave the focus. However, due to recent changes, it needs to only do so when there is only one unique item in the map which it matches the input.
I have a large array defined as following:
var myArray = [
[content, content],
[content, content],
...
]
Later in my code I associate it with a map, at least this is what most stackoverflow questions I looked at referred to it as follows:
var myMap = {};
for(0 to myArray.length) {
var a = myArray[i][0];
var b = myArray[i][1];
myMap[a] = b;
}
Now, finally I iterate over this array as follows:
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0)
the above is the line of code I am struggling to figure out how to change. At this moment, while using on blur, if I type in the letter 'A' for example, and leave the focus area it will automatically fill it in with a certain name. However, in the array there are many other objects that begin with, or contain A. How can I change it so that the onkeydown event will keep going until it finally filters it down to to only possible key-value pair? I tried looking at MDN's documentation for filtering, but I do not think that will work for my purposes, or at least I am too inexperienced with JS.
If the indexOf the first and last are nonnegative and equal, there is just one. You could do this with an && and boolean short circuit evaluation, but that will run very far right off the screen, so I am showing your code with one more nested if (up to you to add the end of the block). But we also need to see if there are matches on multiple keys.
var matchCount=0;
for (var key in map) {
if (map.hasOwnProperty(key)) {
if (map[key].toLowerCase().indexOf(location.toLowerCase()) >= 0){
if (map[key].toLowerCase().indexOf(location.toLowerCase()) == map[key].toLowerCase().lastIndexOf(location.toLowerCase())) {
matchCount++;
then outside your for loop:
if (matchCount==1){ //do your stuff

Reset JS array and set dynamic key array with one line of code?

EDIT
The following question How do I empty an array in JavaScript? does not answer the question i.e. I get how to set / reset an array but that was not my question. I want to to do is:
set the key with a dynamic value e.g. UUID and,
then set element[0] (the above) to true in one line of code.
QUESTION
Code base: Angular 1.5x, lodash 4.x
Seems like a basic question but cannot find an answer. Is it possible using JS, Angular or lodash to reset an array and set it at the same time per the example below. As per the example below the array will keep pushing unless I set reset it. Why you ask, example if I'm using a UUID as key in the HTML e.g.
HTML
<li id="{[{contactus.app_id}]}"
ng-show="view.state[item.uuid]"
ng-repeat="item in collection.items track by item.uuid">
</li>
JS (Angular)
NOTE: this code works and I could also use replace $scope.view.state.length = 0; with $scope.view.state = []; but my question is more along the lines of reseting an array, adding a dynamic key and setting it to true all in one line of code. The complexity is in the dynamic key.
$scope.view = {state:[]};
$scope.setViewState = function (id) {
// how can I collapse the following 2 lines of code into one
$scope.view.state = [];
$scope.view.state[id] = true;
};
You can use computed properties to do this in one line:
$scope.view.state = {[id]: true};
However, this will only work on the latest browsers (Chrome, Firefox, Safari 7.1+, and Edge) and if you have any build steps that need to parse your code they will also need to be upgraded to understand the syntax as well.
If you need older browser support (or if your build tools cannot be upgraded) then you'll need to do it in two steps. However, you can hide those steps in a function:
function initialState(key, value) {
var result = {};
result[key] = value;
return result;
}
then you can simply do:
$scope.view.state = initialState(id, true);

Push and Unshift Operation in javascript object

I have the following dataset
Input:
dataset[0]=[{data:29, color:"y"},{data:44, color:"g"}]
dataset[1]=[{data:16, color:"r"},{data:23, color:"m"},{data:23, color:"b"}]
I am showing this information on bar chart, however bar chart attempting to group them. And it does not give me what I expect. http://jsfiddle.net/7dhb4jh0/1
Therefore,I need to have the following output before I feed my bar chart
The logic behind the desired output to match the length of two dataset by adding {data:0, color:null} There are two things involved unshift and push operations
Desired Output:
dataset[0]=[{data:29, color:"y"},{data:44, color:"g"},{data:0, color:null},{data:0, color:null},{data:0, color:null}]
dataset[1]=[{data:0, color:null},{data:0, color:null},{data:16, color:"r"},{data:23, color:"m"},{data:23, color:"b"}]
Initial Attempt
I have did it as follows, https://jsfiddle.net/s8mywm33/1/
dataset=[];
dataset[0]=[{data:29, color:"y"},{data:44, color:"g"}]
dataset[1]=[{data:16, color:"r"},{data:23, color:"m"},{data:23, color:"b"}]
sum1=dataset[0].length
sum2=dataset[1].length
for(i=0;i<sum1;i++)
{
dataset[1].unshift({data:0, color:null})
}
for(i=0;i<sum2;i++)
{
dataset[0].splice(2, 0, {data:0, color:null});
}
console.log(dataset[0]);
console.log(dataset[1]);
However is there a better way to do it?
As per my recommendations in the comments, there are a number of ways you could be doing this. The one I'd recommend is just using a dummy value (as I mentioned here):
var blank = {data:0, color:null};
var dataset = [];
dataset[0] = [{data:29, color:"y"}, {data:44, color:"g"}, blank, blank, blank];
dataset[1] = [blank, blank, {data:16, color:"r"}, {data:23, color:"m"}, {data:23, color:"b"}];
This makes your intentions visibly clear for what data should be where, and doesn't require that much extra code.

Changewheel functionality How it works?

First of all I want share my appreciation for the amazing work done with this snippet (it's really really a cool tool).
I am trying to use mobiscroll in an app that I am currently developing. I love how easy is to use and customize the mobiscroll tool.
What I really find just obscure, is how use the functionality changeWheel. I tried many things but never with success.
What I am trying to accomplish is to change the values of the first wheel (change completely and regenerate the first wheel), when in my second wheel I select (actually what I want is an on change event) one of the two parameters present.
I build up a function that create the arrays for the two wheels, and that change array of the first wheel relatively to the index of the second wheel returned. (I can see that the function works, and that it generate the exact values for the wheels).
How then I can implement this with the onchange event on the inst, using the function changewheel???
Is this function changewheel suppose to do what I am looking for?
To be very clear, think as an example:
first wheel weight of a person
second wheel unit of measurement (kg or Lbs)
If I change on the spot Lbs to Kg, I want that the first wheel reflect the changes (updating the values available) - if for example i set the limit weight between 0 and 80kg the correspondent value in lbs need to be between 0 and 177lbs (the whole conversion is handled separately with another customized function, and it works exactly as I want).
I can't figure out how instead implement the changewheel event....an example or some more deep explanation will be really useful.
Thank you so much
Best
Dinuz
here goes a simple explanation, if you want to update the values of a diferent whell you need to pass the instance of the mobiscroll and call the method options
$(function(){
var filtersMetrics = [{}];
var wheelPosMetrics = []
var wheelPosFunc1 = [];
var wheelPosFunc = [];
wheelPosMetrics[0] = "Connection";
wheelPosMetrics[1] = "Shared";
wheelPosMetrics[2] = "Deleted";
filtersMetrics[0]['Metrics']=wheelPosMetrics;
wheelPosFunc1[0] = "Count";
wheelPosFunc[0] = "Count";
wheelPosFunc[1] = "Sum";
wheelPosFunc[2] = "Avg";
filtersMetrics[0]['Func']=wheelPosFunc1;
var metricsScroller = $("#select").mobiscroll().scroller({
theme : 'android',
display : 'inline',
mode: 'scroller',
wheels: filtersMetrics,
rows: 3,
'onChange' : function(v, i , e){
if(v[0]==1){
filtersMetrics[0]['Func']=wheelPosFunc;
metricsScroller.mobiscroll('option', 'wheels', filtersMetrics);
}else{
filtersMetrics[0]['Func']=wheelPosFunc1;
metricsScroller.mobiscroll('option', 'wheels', filtersMetrics);
}
}
});
});
I have used the 2.5 version
I also wasn't able to get changeWheel to work, but I found a workaround (I'm using Mobiscroll v2.15.1).
In the onChange–Handler you can set the default values dynamicaly:
.
.
.
onChange: function (values) {
// here your logic for the current values
if (/*here your condition*/) {
// Example for three wheels:
var curWheel = ["1", "2", "3"];
// set the default values
$(/*your element*/).mobiscroll("setArrayVal", curWheel, true);
}
},
.
.
.

Categories

Resources