require.js
IntelliSurvey's script space is governed by a module/file loader called require.js. require.js helps define the scope (i.e., how far variables and references can reach) of the various scripts within a survey page environment to help prevent namespace overlap and interference. For example, require.js prevents jQuery from offering its "$" functionality willy-nilly to just any part of a survey page.
For survey authors, this means that particular syntax protocols must be followed in order to use the system-provided jQuery library (and others). Without invoking require.js in a wrapper, user-written scripts will load on the page without knowledge of or access to the system's jQuery files. However, by wrapping scripts in require's syntax, you'll have access to "jquery", "jqueryui", and a few other libraries. We'll discuss below how to do this, as well as how to include your own custom invocations of CDN-sourced JS files.
See also the require.js homepage for official documentation.
To invoke a jQuery script:
<script type="text/javascript"> require(['jquery'], function($) { $(function () { // your jQuery code goes here, e.g., $('#Q1R1').hide(); }) }) </script>
To invoke a jQuery UI script
<script type="text/javascript"> require(['jqueryui'], function($) { $(function () { // your jQuery UI code goes here, e.g., $( "#somediv" ).dialog(); }) }) </script>
To invoke a jQuery-powered script using a custom widget / library stored in the survey's /s/ drive
<script type="text/javascript"> require(['jquery','/s/mysurveyid/nameofjsfile.js'], function($) { $(function () { // the jQuery & other-script-powered code goes here; script is stored in survey's location (using the same domain, which is important). }) }) </script>
To invoke a jQuery-dependent script / library from a remote domain like a CDN
When the script originates from another domain like a CDN, you can hard-code the entire script path. Here is an example showing how to interact with the Vimeo API:
<script> require(['jquery','//player.vimeo.com/api/player.js','jqueryui'], function($, Vimeo) { $(function() { var ifm = $('iframe').get()[0]; var player = new Vimeo(ifm); $( "#dialog_[% $id %]" ).dialog({ autoOpen: false, modal: true, }).dialog({ open: function onOpen(){ player.play(); }, close: function onClose(){ player.pause(); } }); }); }); </script>
Alternatively, you can create a survey-local copy of require_base_overrides.tx
(find the original in the theme/system/base directory). In this file, you can make modifications to the requirejs configs such as assigning new path names. For example:
%% # == %% # When we want to make local changes to require, we need to do so after require-base is defined AND before it's used. %% # This file allows users to make those edits at the right time %% # == %% # e.g., Add some script to use in my survey (or override the local version of jQuery, etc.): %% # <script type="text/javascript"> %% # require.paths.myscript = ['//cdn.file.min'] %% # require.paths['script/fadein'] = ['/s/[% get_appid() %]/fadein']; %% # </script>
Remove the '%% #' on the last four lines and update myscript with the script's name, and update the path to the script in the square brackets.
This example override file updates jQuery to 3.2.1:
<script type="text/javascript"> require.paths.jquery = ['https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min'] </script>
To include a jQuery script designed to manipulate a card sort:
<script type="text/javascript"> require(['jquery','script/cardsort'], function($) { $(function () { // some code that manipulates card sort elements here }) }) </script>
Try looking at browser source for the require() calls the system renders by default. These can provide clues about what scripts and their locations that are being used (such as 'script/cardsort').
The above patterns should handle most of the common use-cases for jQuery referencing in user/custom scripts.
How to define a function to call again later
A special case that represents something of a challenge is how to define a function within require.js and later reference it via a one-line function call. This isn't terribly simple to do. You either have to remain within the originally defined function's namespace to call the function by name, or affix it to a globally recognized object for safekeeping.
The following example will address how to
- Define a
limitCheckbox(qid,num)
function in require.js
...then later... - Invoke the function (with its "question ID" and "# of options allowed" parameters) in a concise function call.
Methods and failure examples below.
Method 1
This works, because the function call is inside the original definition's scope:
<script type="text/javascript"> require(['jquery'], function ($) { var limitCheckbox = function(ques,max) { $(document).on("click change",function() { var cb = $("input:checkbox[id^=" + ques + "-]"), u = cb.filter(":checked").length >= max; cb.not(":checked").attr("disabled",u); }); } limitCheckbox('Q1',2); }) </script>
Method 2
This works because we attach our function to the $ (jQuery) object; then we'll know where to find it again later. We still need to use require.js, though, when we want to invoke it again:
<script type="text/javascript"> require(['jquery'],function($) { $.limitCheckbox= function(ques,max) { $(document).on("click change",function() { var cb = $("input:checkbox[id^=" + ques + "-]"), u = cb.filter(":checked").length >= max; cb.not(":checked").attr("disabled",u); }); }; }) </script> <script type="text/javascript"> require(['jquery'],function() { $.limitCheckbox('Q1',2); }) </script> # or as one line, though not terribly readable: # <script type="text/javascript">require(['jquery'],function() { $.limitCheckbox('Q1',2); })</script>
Fail 1
This does NOT work. The function definition cannot be found. Usually a defined function would be saved in the DOM-level namespace to be referenced again, but require.js prevents (i.e., that's its job).
<script type="text/javascript"> require(['jquery'], function ($) { var limitCheckbox = function(ques,max) { $(document).on("click change",function() { var cb = $("input:checkbox[id^=" + ques + "-]"), u = cb.filter(":checked").length >= max; cb.not(":checked").attr("disabled",u); }); } }) </script> <script type="text/javascript"> limitCheckbox('Q1',2); </script>
Fail 2
This also doesn't work. require.js is creating little distinct namespace areas that don't talk to each other unless you actively reach outside the default scope and attach your functions to global objects (as in Method 2 above):
<script type="text/javascript"> require(['jquery'], function ($) { var limitCheckbox = function(ques,max) { $(document).on("click change",function() { var cb = $("input:checkbox[id^=" + ques + "-]"), u = cb.filter(":checked").length >= max; cb.not(":checked").attr("disabled",u); }); } }) </script> <script type="text/javascript"> require(['jquery'], function ($) { limitCheckbox('Q1',2); }) </script>
Comments
0 comments
Please sign in to leave a comment.