The Instructure Community will enter a read-only state on November 22, 2025 as we prepare to migrate to our new Community platform in early December. Read our blog post for more info about this change.
Found this content helpful? Log in or sign up to leave a like!
Are there any resources about how to do custom javascript for mobile devices? For instance, does jQuery work in mobile, or is there some other framework that should be used? How do you call the Canvas API from your javascript code? When writing data to the screen, do you use HTML or is there some other markup that is needed?
I recently had a request from a student to make a feature, that we have available on the web, available in the mobile app. I thought it might be as easy as taking that javascript code into a new javascript override file and adding that file in the Theme editor. But that doesn't seem to be working, so any help would be appreciated.
Also, how do people go about debugging javascript on mobile devices?
Thanks,
Mike
I have been playing around with this a bit more this afternoon and have been able to answer at least couple of my questions.
You can't use jQuery, so you have to resort to using pure javascript. (So you have to use document.getElementById to get an element instead of using the jQuery $('#id') statement.)
It appears you can use HTML when writing data to the screen.
I am running into an issue when trying to call the Canvas API. I am using the XMLHttpRequest object, but I am getting back a 401 response code. I have tried using an access_token in the request, but that hasn't helped. So if someone could point me in the right direction about how to do that, I would appreciate it.
Thanks,
Mike
I have made some more progress today and currently only have one issue that I need some help in resolving. As I stated above, I can use pure Javascript to get data and write data in the mobile app. I have figured out the issue I was having with my AJAX call returning a 401. (The issue was that the relative url that I was using was incorrect. And I don't need to include the access token.)
The final issue that I currently have is, when I am in a course in the mobile app, how can I get the course id for that particular course? On the web, I could use one of a number of techniques, but none of them seem to work in the mobile app. Here's what I have tried thus far:
document.location
document.URL
document.documentURI
ENV.COURSE_ID
ENV.COURSE.id
ENV.MODULES_PATH
ENV.WIKI_PAGES_PATH
None of these seem to work. The document based options just return the base url. The ENV based options don't return anything. (I suspect that ENV isn't defined in the mobile app.)
Any guidance in how to get the current course id would be appreciated.
Thanks,
Mike
I'm sorry I don't have anything for you. I tried looking through the mobile source code to see where the course ID is found, but I'm not a mobile developer and it looks foreign to me.
I wanted to share that even though you're not getting much help from us, that I really appreciate you coming back and updating us with what you have found so that we'll know in the future.
@James ,
Thanks, I do appreciate hearing back from someone. And hopefully what I have figured out thus far will be helpful to someone else in the future.
You mentioned that you looked through the mobile source code. Is that somewhere that I would have access to look at? I may not be able to make any sense of it either, but it couldn't hurt for me to try.
Thanks,
Mike
The main GitHub site for Instructure is https://github.com/instructure
There are 195 repositories there, so I'm not sure if I'm giving you the right ones, but here's what I think are the main code, the iOS verison, and the Android version.
Thanks, I'll take a look as time allows. If I figure anything out, I'll make sure to post back here to share any knowledge that I gain.
And, perhaps, someone from the mobile development team might see our thread and chime in with some thoughts.
Thanks again!
Hi @fariborzm . I can't tell you how excited I am to hear that you can make ajax requests to the Canvas API from JS (although I'm not sure how excited I am by the idea of having to write them with plain JS instead of JQuery in the app!) as I had been led to believe that this wasn't possible - we use this extensively in our current VLE, Sakai, for things like surfacing the current week's materials, rather than making students have to go and look for them.
I've just come across this page: Getting Started With The Api: Canvas Dev and Friends which suggests that I can read from the api with whatever rights the logged in user has:
- does that sound right?
- does that mean that I'll only need an access to token if I want to carry out POST/PUT operations?
- is there any information anywhere on how to generate an access token? I am still very confused as to how I can do this safely as I can't see how I'm not going to have to leave potentially sensitive information lying around in order to get that token...I'm not talking about writing a third-party e.g LTI application here, just calling Canvas api methods from within Canvas pages...
I'd also be very interested in hearing about your progress with the app. I've just spent a very frustrating morning attempting to debug the Canvas Student App using Chrome (blogged here) and finding out that, while the course Pages are html (with mobile theme .css and .js applied) and shown with WebView, the Modules page seems to have been written in native code. This is particularly annoying as, unlike the web version, it appears that only pages which are launched directly from this native Modules page have the next and previous navigation...
Hello theotherdy,
Doing AJAX without jQuery is a little messier, but definitely doable. Here's some example code that might help you get started:
var url = '/api/v1/users/self'; //Relative url to the current user's user information in the Canvas API.
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200){
var resp = this.responseText;
if(resp.startsWith('while(1);')) {
resp = resp.substring(9);
}
var data = JSON.parse(resp);
//data is now a javascript object that has properties that contain the current user's user information.
}
};
xhr.send();
You can replace url with whatever API endpoint you want to use from the Canvas API. As you can see, I do not include an access token. Since the code is executing in the context of the current user in the mobile app, it runs using their access level.
In this situation, I think you only need to use an access token, if the current user doesn't have access to make updates through the Canvas API.
As far as creating an access token, I created mine so long ago, I don't remember how I did it. It was in the prior version of the Canvas UI and things have moved around quite a bit since then. I thought it was associated with a particular user (most likely an admin), but I don't see it when I jump into Canvas. The Developer Keys tab might be where you do it now, but I'm not sure. In the end, unless you are doing updates to Canvas from outside of a currently open session with the Canvas servers, you probably don't need to have an access token. (For instance, I use mine to synchronize student / faculty information from our HR/SIS systems with Canvas. In that context, I don't have an established session with Canvas, so the access token allows me to use the API endpoints without having to log in.)
Hope this helps.
Mike
Thanks @fariborzm , that's very helpful indeed and has opened up all sorts of possibilities which I can't wait to explore. If I come across anything that gives me the current course id from within the app, I'll let you know.
Thanks again
Damion
The "while(1);" appears when there is no "Content-Type: application/json" header with the request. Adding this line before your .send() will tell Canvas that your application can handle JSON and it won't send it.
xhr.setRequestHeader('Content-Type', 'application/json');Also, if you're wondering how to display code here in the Community, you go to the More button on the toolbar and choose Syntax Highlighter.
I am not sure if this is available through the mobile apps, but there are some improvements in JavaScript that make the pure JavaScript approach of fetching data easier to handle. The fetch method allows you to set up a promise, which greatly simplifies things. I've not used it in any code I've published -- just started using it in some test code.
I've just come across this page: Getting Started With The Api: Canvas Dev and Friends which suggests that I can read from the api with whatever rights the logged in user has:
This is for the web UI through a browser. You don't need an access token through the web interface because there is a CSRF token called _csrf_token that provides credentials. It is a cookie and included as part of requests and often returned as part of the response. The jquery exposed from Canvas has modified the AJAX setup to automatically send that for you so that you don't need to include it.
For example, here are the cookies that were sent to Canvas and what came back when I loaded home page for one of my courses.
There were two times that I had to manually include a CSRF token. I may not have needed to in one case, but I'm pretty sure it wasn't working until I did.
One of those was with a POST through jQuery's .ajax method ($.ajax). This is the code I used to obtain the _csrf_token from the cookie list.
function getCsrfToken() {
var csrfRegex = new RegExp('^_csrf_token=(.*)$');
var csrf;
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
var match = csrfRegex.exec(cookie);
if (match) {
csrf = decodeURIComponent(match[1]);
break;
}
}
return csrf;
}The other place I had to use it was when I was doing it through pure JavaScript. This is a snippet -- the url, parms, and csrf were already assigned.
var jparm = JSON.stringify(parms);
var xhr = new XMLHttpRequest();
xhr.open('PUT', url);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.setRequestHeader('X-CSRF-Token', csrf);
xhr.send(jparm);Again, this is all done with the web UI through a browser, I'm not sure how much of it works for mobile, but it might give you something to go off of.
Thanks again for your help with this. I've finally got around to trying api calls from within the theme JS - I'm stuck with plain Javascript as this is eventually destined for the mobile app. This is what is working for me so far:
var csrfToken = getCsrfToken();
console.log('crsfToken', csrfToken);
fetch('/api/v1/conversations/unread_count',{
method: 'GET',
credentials: 'include',
headers: {
"Accept": "application/json",
"X-CSRF-Token": csrfToken
}
})
.then(status)
.then(json)
.then(function(data) {
console.log(data);
})
.catch(function(error) {
console.log('Request failed', error);
}
);
/*
* Function which returns csrf_token from cookie see: https://community.canvaslms.com/thread/22500-mobile-javascript-development
*/
function getCsrfToken() {
var csrfRegex = new RegExp('^_csrf_token=(.*)$');
var csrf;
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = cookies[i].trim();
var match = csrfRegex.exec(cookie);
if (match) {
csrf = decodeURIComponent(match[1]);
break;
}
}
return csrf;
}
/*
* Function which returns a promise (and error if rejected) if response status is OK
*/
function status(response) {
if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response)
} else {
return Promise.reject(new Error(response.statusText))
}
}
/*
* Function which returns json from response
*/
function json(response) {
return response.json()
}You'll note that I'm using the fetch() method as I like working with promises - fingers crossed that it'll work in the app!
Some other things to note:
However, all I've got it to do so far is log to the console....onwards!
Thanks for coming back and letting us know what is working.
Although I'm not writing for mobile, I wrote my first major script using fetch() and promises last week (maybe 2 now). It was an interesting experience learning them [still would not call myself an expert on them] and it was nice to not have to be tied to jQuery. I went all out (for me) and used let, const, and arrow operators. Now I need to explore async and await and see if they help or just make it easier to understand.
I've done a few Angular 2+ clients using typescript where let and => (to get around 'this' issues) are the standard but had not really thought about using these in plain JS - thanks for the tip...some more investigating to do I think!
Thanks again for all your help.
Hello Everyone,
I wanted to circle back around with what I have found thus far about extracting the course id via javascript in the mobile app. I reached out to our CSM and asked her if she could ask the developers this question. She got back to me this afternoon and said that she was unable to find anyone who knew how to accomplish that from within the mobile app.
Perhaps that is something they will add into a future version of the app, but for now it would seem that this is not possible.
Thanks,
Mike
Thanks @fariborzm , had also asked our CSM who was going to refer to the 'product team' but never heard back. I guess as a fall-back, we could create a sub-account for every course, so we can include the course ID in the JS, but that's hardly ideal! I note that, in the browser anyway, pages include a class (context_course_x) on the body tag:
<body class="with-left-side course-menu-expanded with-right-side padless-content pages home primary-nav-expanded full-width context-course_57 lato-font-not-loaded-yet show webkit chrome no-touch">which includes the course Id..haven't yet looked whether that's retained on the app...and may not be the most reliable interface to pin anything on...
Have you created a feature request which we can vote for?
theotherdy, I had never noticed that class in the body tag, nice find! Unfortunately, I wasn't able to get that to work in the mobile app either.
I just created an idea to add this type of functionality to the mobile app, it's located here -> https://community.canvaslms.com/ideas/11388-mobile-app-make-contextual-data-available-via-javascript , If anyone would like to up vote it, I would appreciate it.
And if anyone happens to figure this out, I would appreciate a post to this thread.
Thanks,
Mike
Off topic, but I thought it was worth a shot:
I can't even get a simple alert to run in the apps. How can I make sure the js-file is loaded in the app at all?
Hi @Hans_Magnus . Took me a while to figure out that there's a separate Mobile app JavaScript file upload box under Upload when you edit a theme.
And should add that it's possible to debug those parts of the app which will use your uploaded JS (as far as I can see some parts, including Modules, don't run as web pages) using Chrome's Remote Debugging on Android. I wrote a blog post about doing this here: Remote Debugging Samsung Galaxy 7 Edge with Chrome – MSDLT Blog but have to admit that it's much simpler on a Mac if you have access to one.
I know, I have used the form correctly ![]()
The other files targeting the desktop browser version works as expected.
Sorry, should have realised you'd probably already tried that! And are you using plain JS - JQuery isn't loaded in the app?
Using plain JS, I am aware that there is no jQuery support in the app.
However, I just got the alert to show up when I use the Teacher app, and hit Edit page. So the script loads, sometimes at least.
Also, there seems to be some sort of cache issue. When I load CSS, it takes forever for the changes to kick in, and it seems to be happening randomly.
I'm testing the iOS apps, and I use a Mac. How do I access debugging tools for these apps?
Thanks for replying btw ![]()
I'm afraid I haven't got as far as testing on iOS yet but this looks like it might do what you need: https://medium.com/@channaly/how-debug-cordova-based-application-with-chrome-dev-tool-43e095a735b4 ....the app certainly seems to use WebKit on Android. Hopefully getting Safari into debugging mode will help with the caching issues...but I'm guessing
The script also loads when I go to Discussions > Edit Discussion on the Teacher app. So it seems like my script only runs when the text editor loads.
Could you share the relevant bit of your script on Gist or similar?
Not much to share really,it's just an alert box. Nothing fancy, looks like this:
:
Hi all. I have been meaning to pull this together to share for quite awhile but better late than never. I have done a some playing with custom CSS and JavaScript on the mobile app but am far from knowing everything I would like to know and debugging the mobile app has been an absolute pain so any suggestions would be appreciated.
There are a few things you should know before I share some code:
Okay, that being said, lets look at some code. This is what you can upload to Canvas to load jQuery before processing your code:
(function () {
// The following function will retrieve and load a JavaScript file
// I believe I found this at https://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/
function loadScript(url, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
// First load jQuery and then load jQuery UI
loadScript("https://code.jquery.com/jquery-1.9.1.min.js", function () {
loadScript("https://code.jquery.com/ui/1.11.3/jquery-ui.min.js", function () {
//jQuery and jQuery UI loaded so you can run jquery code here
});
});
})();I have been using this code for a few years and it loads jQuery version 1.9.1. I just checked and Canvas loads version 1.7.2 into the browser but I have not noticed any issues from that difference.
Another thing that you should know is that I hate having to continually upload files into the theme editor. Because of that, I actually keep the code above in a file on an external server and upload a variation to Canvas that loads the external file. It looks something like this:
// Load APP JS File
(function () {
// The following function will retrieve and load a JavaScript file
// I believe I adapted this from code found at https://www.nczonline.net/blog/2009/07/28/the-best-way-to-load-external-javascript/
function loadScript(url, scriptID, callback) {
var script = document.createElement("script");
script.type = "text/javascript";
script.id = scriptID;
if (script.readyState) { //IE
script.onreadystatechange = function () {
if (script.readyState == "loaded" || script.readyState == "complete") {
script.onreadystatechange = null;
callback();
}
};
} else { // other browsers
script.onload = function () {
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
// using today as a variable helps keep this from loading a cached version
var today = new Date(),
appScript = document.getElementById('inst_app_script');
// only load if the script doesn't already exist or if jQuery is already defined
if (appScript === null && window.jQuery === undefined) {
loadScript("https://<path to js file>?" + today.getDate(), 'inst_app_script', function () {
// I log this so I can see it if it shows up in the browser
console.log('Global App JavaScript Ran');
});
}
})();The script is given an id that can be checked to make sure doesn't get added more than once. It also looks to make sure jQuery isn't already defined, this helps prevent the code from running in the browser if it somehow finds its way there. There was an issue in the past where Canvas loaded the mobile app assets twice for every discussion response in the browser. It made Canvas discussions that had more than a couple of posts pretty much unusable. I also add a little code in the browser JavaScript to remove the app stylesheet if it ever appears.
A few other thoughts that I can share that might be helpful (or might be out of date).
try {
// Run some code
} catch (err) {
alert(err);
}$('body').prepend('<textarea id="fillMe" style="width:50%;height:50px; margin:auto;"></textarea>');
$('#fillMe').val($('html').html());Anyway, those are the approaches that I can think of that I have used to work with the mobile app that might be helpful. I have not been able to dedicate as much time as I would like to try and figure things out. This has just helped me get some of the essentials working.
Thanks @kenneth_larsen , just used this to great effect for embedding gists in Canvas pages: Embedding a GitHub gist in Instructure Canvas – MSDLT Blog
I just talked to @peytoncraighill and some of the guys at the Mobile booth at InstructureCon. None of them knew of any information that was being exposed. They hinted that it may be possible to do this as long as it didn't create security issues. Maybe Mike's feature idea https://community.canvaslms.com/ideas/11388-mobile-app-make-contextual-data-available-via-javascript could use some loving.
They did say that for user created content, one way would be to add a div with an id or class that could be used for identifying some things like the course ID. That still doesn't help with the user context.
Thanks @James for the update. I'm afraid that I've been beginning to think the unthinkable and am considering hard-coding the Course ID into the custom JS for a sub-account. It would require a sub-account for every Course but might be worth it as it would then allow me to use my Dashboard-style tiles for modules in a Home Page which works on both web and app...
Community helpTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign inTo interact with Panda Bot, our automated chatbot, you need to sign up or log in:
Sign in