Custom web component for online experiments: Forms, surveys, questionnaires, and other web-based content

The form.io editor can create those for you right? Just copy-paste the output and put it in schema.json. Or maybe I misunderstood you?

Wow, brilliant, you didn’t misunderstand me, I just didn’t know form.io existed!

Good to know! @arnon_weinberg had warned me before my documentation is a bit too technical :). I’ll go rewrite it to make it more accessible

Hi again, @thomas_pronk, I’m wondering whether you know how to specify a certain number of questions per screen, and participants press ‘Next’ to move forward to the next set of questions, and the answered questions will be saved? I know PsychoPy is not intended as a surveying platform, but my study has 3 parts, the first one is a survey and 2 other PsychoPy experiments, I don’t want to lose participants across platforms. Thank you for your help!

How about 3 copies of the form.io form? You’d need a copy of the right routine in the experiment, an index.html for each, and have each point to the right schema.

Hi Arnon,

I’ve manged to set the width to 85% of the screen width using JavaScript in my demographics demo. However, when I add more questions to the html page I end up with a double scroll bar like you can see in @APW 's screenshot. Do you have any ideas of how to limit the page to a single scrollbar?

Best wishes,

Wakefield

Hi @wakecarter,

I am not seeing a double scrollbar in my testing (tried both Firefox and Chrome):

I am not sure what would cause that for you. Maybe if the frame content size is modified after loading…? I would probably use a process of elimination (remove code from html until problem goes away / add code until it returns) to determine the cause, but I would need to be able to replicate the issue first.

1 Like

Thanks. I think I’ve sorted it by changing

 $('#iframe-m').width($(this).contents().find('html').width()+20);
 

to

 $('#iframe-m').width($(this).contents().find('html').width()+100);

There may be a better way of setting it. What I want it a percentage of the actual screen / window size.

I’m currently using the following in PsychoPy

expInfo['xRes'] = screen.width;
expInfo['yRes'] = screen.height;

let src = ('demographics.html?xRes='+expInfo['xRes']+'&yRes='+expInfo['yRes']);

and then the following to set the width and text height in the html file.

	var xRes = getUrlParam('xRes','Empty')*.8;
	var yRes = getUrlParam('yRes','Empty')*.04;

and then the following to set the body size.

<body id="bodyForm">
	<script>
		document.getElementById('bodyForm').style = "width: " + xRes + "px; font-family: Arial, Helvetica, sans-serif; justify-content: left; background-color: grey; color: white; font-size: "+ yRes +"px";
	</script>

Good morning,
first of all much thx! Your posts are very helpful
@arnon_weinberg
@wakecarter

I’ve posted a new topic regarding the Timing in embedded html
Timing in embedded html (iframe) / PsychoJS vs. JS - Online experiments - PsychoPy

I think this question would also be interesting for other people that would like to use embedded pages and measure rt.

It would be nice if you could take a look at my question

Best wishes
PJ

I’m still having difficulty with getting the scroll working on Safari.

Any ideas what I’m doing wrong here @arnon_weinberg / @thomas_pronk ? My code is trying to resize the text inside the form based on the screen size.

Begin Routine JS

expInfo['xRes'] = psychoJS.window.size[0];
expInfo['yRes'] = psychoJS.window.size[1];

let src = ('aq.html?xRes='+expInfo['xRes']+'&yRes='+expInfo['yRes']);

params = {};  // Results added here after form is submitted
continue_routine = true; // Routines can't be ended from within Begin Routine
$(document).ready(function() {
    // Add custom contents from html file using an iframe:
    $('body').append('<div id="iframe-o" style="visibility: hidden; position: relative; display: table; margin: auto;width:90%;overflow-x: hidden;"><div id="iframe-m" style="display: table-cell; vertical-align: middle; overflow-y: auto; overflow-x: hidden;"><div id="iframe-i" style="display: inline-block; width:100%; overflow-y: auto; overflow-x: hidden;"><iframe id="iframe" src="'+src+'" frameborder="0" style="width: 100%; overflow-y: auto; overflow-x: hidden; "></iframe></div></div></div>');

    $('#iframe').on('load',function(iframe){
        // Auto-adjust iframe size:
        // Auto-adjust iframe size:
        $(this).contents().find('html').css({ 'display': 'table', 'width': '90%', 'overflow-x': 'auto' });
        $('#iframe-o').height($(window).height()-20, true);
        $('#iframe-m').width($(this).contents().find('html').width()+100);
        $('#iframe-i').height ( Math.max ( $(this).contents().find('html').height()+20, $(window).height()-20 ), true );
        $(this).height($(this).contents().find('html').height());
        $('#iframe-o').css('visibility','visible');

        // If iframe contains a form, then capture its output:
        $(this).contents().find('form').on('submit',function(e){
            e.preventDefault();
            $.each($(this).serializeArray(),function(i, param){
                params[param.name] = param.value;
                psychoJS.experiment.addData(param.name, param.value);
            });
            console.log ( 'DEBUG:FRM', params );
            // Remove iframe and continue to next routine when done:
            $('#iframe-o').remove();
            continue_routine = false;
        });
    });
});
//$('#iframe').attr( 'src', function(i,val){ return val;} );

Then in the html file itself

  <!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="user-scalable=no">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js">
	</script>
<script type="text/javascript">
	function getUrlVars() {
		var vars = {};
		var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
		vars[key] = value;
		});
		return vars;
	}
	function getUrlParam(parameter, defaultvalue){
		var urlparameter = defaultvalue;
		if(window.location.href.indexOf(parameter) > -1){
        	urlparameter = getUrlVars()[parameter];
	        }
		return urlparameter;
	}
	var xRes = getUrlParam('xRes','Empty');
	var yRes = getUrlParam('yRes','Empty');
if ((xRes > yRes)) {
    xRes = (xRes * 0.8);
    yRes = (yRes * 0.04);
} else {
    if ((xRes < yRes)) {
        xRes = (xRes * 0.95);
        yRes = (yRes * 0.08);
    } else {
        xRes = 1000;
        yRes = 30;
    }
}

</script>
<style type="text/css">
<!--
	.style1 {font-size: 40px}
	.style2 {text-align: left}
	input.checkbox { 
		width: 40px; 
		height: 40px; 
	} 
	input[type=radio] { 
        	width: 40px; 
		height: 40px; 
	} 
	.button {
		background-color: #4CAF50;
		border: none;
		color: white;
		padding: 20px;
		text-align: center;
		text-decoration: none;
		display: inline-block;
		font-size: 40px;
		margin: 4px 2px;
		border-radius: 12px;
	}
-->
</style>
</head>
<body id="bodyForm">
	<script>
		document.getElementById('bodyForm').style = "width: " + xRes + "px; overflow-x: hidden; font-family: Arial, Helvetica, sans-serif; justify-content: left; background-color: grey; color: white; font-size: "+ yRes +"px";
	</script>

Thanks in advance for any suggestions.

Bit hard for me to reproduce, since I run Windows. Still got some (less well-informed) questions and suggestions:

  • Q: What’s going wrong exactly?
  • S: General idea. Use the scrolling features provided by the web-browser instead?

I think that the issue is that no scroll bars are visible and it won’t scroll. There may be a related issue of win.size failing but I’m not sure about this.

I’d love to use the scroll bars within the web browser and in one implementation (see earlier in this thread) I was getting two scroll bars. It might be that I’ve suppressed the wrong one but I’m somewhat out of my depth with what each of the nested iframes are doing.

Just tried it out in in the formio routine of https://gitlab.pavlovia.org/tpronk/demo_embed_html; if I made my Window really small I got a nice (singular) scrollbar.

In this implementation I’m not very concerned what’s happening inside of the iframe. It’s just whatever formio throws in there. The scrollbar is inside of a container div of which I ensure that it fills the window as follows:
<iframe id="iframe" src="formio/index.html" style="width: 100%; height: 100%; position:absolute;z-index:1;top:0;left:0;border:0;"></iframe>

1 Like

After discovering an issue with Chrome on my other laptop I stayed up late to trace and fix an issue with the inequality between width and height being tested alphabetically rather than numerically.

My new code uses parseInt to help this. I’ve also managed to inject a dynamic font size into the css but failed with using the same code for the radio and checkbox sizes.

<script type="text/javascript">
	function getUrlVars() {
		var vars = {};
		var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
		vars[key] = value;
		});
		return vars;
	}
	function getUrlParam(parameter, defaultvalue){
		var urlparameter = defaultvalue;
		if(window.location.href.indexOf(parameter) > -1){
        	urlparameter = getUrlVars()[parameter];
	        }
		return urlparameter;
	}
	var xRes = parseInt(getUrlParam('xRes','30'));
	var yRes = parseInt(getUrlParam('yRes','30'));

if ((xRes > yRes)) {
    xRes = (xRes * 0.8);
    yRes = (yRes * 0.04);
} else {
    if ((xRes < yRes)) {
        xRes = (xRes * 2.4);
        yRes = (yRes * 0.06);
    } else {
        xRes = 1000;
        yRes = 30;
    }
}

</script>
<style type="text/css">
<!--
	.style1 {font-size: var(--yRes)px}
	.style2 {text-align: left}
	.style3 {background-color: #999999;
	}
	input[type=checkbox] { 
		width: 35px; 
		height: 35px; 
	} 
	input[type=radio] { 
        	width: 35px; 
		height: 35px; 
	} 
	.button {
		background-color: #4CAF50;
		border: none;
		color: white;
		padding: 20px;
		text-align: center;
		text-decoration: none;
		display: inline-block;
		font-size: var(--yRes)px;
		margin: 4px 2px;
		border-radius: 12px;
	}
-->
</style>
1 Like