JavaScript,
1 TopicSetVar and GetVar March 2025 Update Question
Before I update my Storyline installation to the March 18, 2025 (Build 3.98.34222.0) build, can someone tell me if SetVar and GetVar will still work? I ask because in the Advanced Storyline support article: https://access.articulate.com/support/article/Storyline-360-Advanced-JavaScript-API ...it mentions in the: "Variables (previously GetVar and SetVar)" section... to use: Retrieve a variable's value: getVar('name') Set a variable's value: setVar('name', value) Will this change break existing courses that use SetVar and GetVar in triggers when a course is opened/re-published?Solved72Views0likes3CommentsObject reference
I only recently started using the JavaScript API beta. I have noticed that sometimes the Object reference connects to the correct object, and other times it does not. Any thoughts on that? It seems that when it does not connect, it displays the top object in the list rather than the one I want. The good news is the script still works.21Views0likes0CommentsHow To Embed An ElevenLabs Conversational AI Widget Into SL360 Using JS!
Hi Heroes, It feels like something new and exciting is always around the corner in the world of generative AI technology, and this week ElevenLabs put themselves firmly in the driving seat of the agentic AI revolution with their new Conversational AI toolkit. If you haven't heard of this yet, check out this video which explains it all: https://www.youtube.com/watch?v=v-EYzZCLF48&ab_channel=ElevenLabs The interactive, animated widget that this toolkit provided is easy to embed anywhere, including directly within an Articulate Storyline 360 project slide! If you're interested in how to get started, I've written a blog post that includes all the steps, including an Execute JavaScript snippet you can use to effortlessly get your agent loaded into your activity: https://discoverelearninguk.com/how-to-set-up-elevenlabs-conversational-ai-widget-in-articulate-storyline-360/ I'm also currently experimenting with the API for the new Conversational toolkit to understand how I can implement it into my eLearning Magic Toolkit plugin for Storyline + WordPress, potentially opening the door for developing real-time voice activated automation all within a Storyline-built eLearning activity! Much more to come very soon. 🚀 --- My name's Chris Hodgson, an eLearning developer and software trainer based in the UK. I enjoy creating fun, unique, and engaging online experiences using Articulate software! Connect with me on LinkedIn - https://www.linkedin.com/in/chrishodgson44/259Views4likes1CommentAssistance Needed: Enabling Easy Copy & Paste for ChatGPT-Generated Vision Statements in Storyline
I created a slide using a ChatGPT-generated response where users input their name, role, goals, and future position. After clicking "Submit," ChatGPT generates a personalized Vision Statement for them. I want users to be able to copy this Vision Statement easily and paste it into a Word document. Is this possible? If so, can someone guide me on how to set this up using JavaScript?Solved46Views0likes2CommentsProgress bar with javascript not working in new versions
Hi all. I am new here. Maybe a beginner question.... I found out that my javascript progress bar doesn't work after the updates past 8th october 2024. I couldn't find out why, tried with the console and different ways. I think the <div> is not visible or not inserted. Could you please check my script in the example file? Probably you have seen this scipt in other projects, its from the web. Behaviour: start slide (without progress bar) 1. slide - master slide 1 - progress bar works fine the first time. 2. slide - master slide 1 - progress bar failes. 3. slide - master slide 2 - progress bar works fine. 4. slide - master slide 1 - progress bar works fine. If I have two slides with the same master slide in a row, on the second slide the progress bar failes. I upload a example with 4 slides. The script is on the master. Any help is welcome. Thank you.159Views1like13CommentsVariable not changing button state
I've got a pretty complicated situation. I am creating a compliance course that a user must be in each module for 30 minutes. There are 9 modules. When the course begins, they can't leave the course, open another browser tab, click on a different monitor screen, etc. Also, if they do any of these, their time stops and resumes when they return to the course and a popup layer tells them that they need to resume and that their time has stopped. Everything works great except that I've got a variable, %correctedElapsedTime%, that shows the actual time that they've been in the course. Essentially, it subtracts the time away from the built in %Project.ElapsedTime% and shows a running timer in the corner of the slide the whole time. I want a button to change states to normal when %correctedElapsedTime% has been met. Right now, I can only get the button to change when it is triggered by %Project.ElapsedTime%. %correctedElapsedTime% is a text variable with a value of MM:SS and %Project.ElapsedTime% is a number variable with a value of 0. I think this is where the problem may be. It also may be that my timer is somehow skipping over the 18000000 ms to meet the 30 min time constraint. Any thoughts? Slide triggers Master slide javascript and triggers Javascript var player = GetPlayer(); var elapsedTime = 0; var timer = null; var isTabActive = true; var isFocused = true; var inactivityTime = 0; var inactivityLimit = 120; var inactivityTimer = null; // Load saved elapsed time if (player.GetVar("correctedElapsedTime")) { var savedTime = player.GetVar("correctedElapsedTime").split(":"); elapsedTime = parseInt(savedTime[0]) * 60 + parseInt(savedTime[1]); } // Update elapsed time function updateElapsedTime() { if (isTabActive && isFocused) { elapsedTime++; var minutes = Math.floor(elapsedTime / 60); var seconds = elapsedTime % 60; var formattedTime = minutes + ":" + (seconds < 10 ? "0" : "") + seconds; player.SetVar("correctedElapsedTime", formattedTime); } } // Start elapsed time counter function startTimer() { if (timer === null) { timer = setInterval(updateElapsedTime, 1000); } } // Stop elapsed time counter function stopTimer() { if (timer !== null) { clearInterval(timer); timer = null; } } // **Inactivity Timer Functions** function resetInactivityTimer() { inactivityTime = 0; // Reset inactivity timer player.SetVar("showPopUpLayer", false); // Hide pop-up if user is active } function trackInactivity() { inactivityTime++; if (inactivityTime >= inactivityLimit) { player.SetVar("showPopUpLayer", true); stopTimer(); } } // **Event Listeners to Reset Inactivity Timer** document.addEventListener("mousemove", resetInactivityTimer); document.addEventListener("keydown", resetInactivityTimer); document.addEventListener("touchstart", resetInactivityTimer); // **Start inactivity tracker** function startInactivityTimer() { if (inactivityTimer === null) { inactivityTimer = setInterval(trackInactivity, 1000); } } // **Detect window focus loss** window.addEventListener("blur", function() { isFocused = false; player.SetVar("isFocused", false); player.SetVar("showPopUpLayer", true); // Show pop-up when window is blurred stopTimer(); }); // **Detect window focus gain** window.addEventListener("focus", function() { isFocused = true; player.SetVar("isFocused", true); player.SetVar("showPopUpLayer", false); startTimer(); }); // **Detect when tab visibility changes** document.addEventListener("visibilitychange", function() { if (document.hidden) { isTabActive = false; player.SetVar("isTabActive", false); setTimeout(function() { player.SetVar("showPopUpLayer", true); // Ensure pop-up layer appears }, 500); // Delay helps trigger the layer reliably stopTimer(); } else { isTabActive = true; player.SetVar("isTabActive", true); player.SetVar("showPopUpLayer", false); startTimer(); } }); // **Start timers** startTimer(); startInactivityTimer();63Views0likes5CommentsJavaScript help needed: button floating animation issue in Storyline
Hi everyone, I'm currently experimenting with creating a floating animation for a button using javascript. The goal is that when learners hover over the button, it will float up slightly, and when the mouse leaves, the button will bounce back to its original position. Here’s the JavaScript code I’m using: let button = document.querySelector("[data-model-id='60P0mJeyWGh']"); button.addEventListener("mouseover", () => { gsap.to(button, { y: -0.5, // Moves the button up by 0.5 pixels (subtle effect) duration: 1, // Animation duration ease: "power1.inOut", // Smooth easing yoyo: true, // Makes it come back to the original position repeat: 1, // Repeats once to go up and back down }); }); button.addEventListener("mouseout", () => { gsap.from(button, { // Uses gsap.from to return to the original position y: 0, // Moves back to the original position duration: 1, // Duration for the return animation ease: "power1.inOut", // Smooth easing }); }); However, when I implement this in Storyline, the button moves WAY more than 0.5 pixels and, after several hover events, ends up moving all the way to the top of the slide. Can anyone review my code and see if there might be an issue? I’m not sure why this is happening. I have also attached the Storyline file in case it would be helpful for review. Thank you!68Views0likes3CommentsDynamic border radius experiment
This was just a little experiment to see if I could manipulate the border radius of shapes on the slide numerically using JavaScript, and the answer is yes. Not sure of any use case, but I've always wanted to be able to control the border radius numerically in Storyline as border radius is not maintained when resizing shapes. I'm not sure if this could help as there is no easy way of targeting specific shapes in a slide without knowing their unique ID. It was a bit of fun anyway. If anybody would like to play or expand on this, go for it. What I've found so far, is it will only support solid or no outlines on shapes. When you start using other styles (dot, dash) an image is used by Storyline to achieve this. window.updateCornerRadius = function(targets, radius) { targets.forEach(function(target){ const path = document.querySelector(`[data-dv_ref="ref-${target}"] path`); if (!path) { console.error(`SVG target ${target} not found.`); return; } // Extract the bounding box of the current path const bbox = path.getBBox(); const x = bbox.x; const y = bbox.y; const width = bbox.width; const height = bbox.height; // Ensure radius doesn't exceed half the width or height radius = Math.min(radius, width / 2, height / 2); // Construct a new path data string with the updated corner radius const newPathData = ` M ${x + radius},${y} H ${x + width - radius} A ${radius},${radius} 0 0 1 ${x + width},${y + radius} V ${y + height - radius} A ${radius},${radius} 0 0 1 ${x + width - radius},${y + height} H ${x + radius} A ${radius},${radius} 0 0 1 ${x},${y + height - radius} V ${y + radius} A ${radius},${radius} 0 0 1 ${x + radius},${y} Z `; // Update the path's "d" attribute path.setAttribute("d", newPathData); }); } An example of this being called. I'm using a variable, borderRadius, change trigger. const borderRadius = getVar("borderRadius"); window.updateCornerRadius(['12','15','18','21'], borderRadius);101Views2likes2CommentsMagic JavaScript switch?
I have a drag and drop quiz with 95 drag options that are then added to a chart. This is all working beautifully and tiling onto the chart very nicely, but if I want to randomize the order that the 95 options appear in (on the drag sequence menu) then I have to manually drag them around to make them appear randomized. Has anyone ever incorporated javascript to make drag options appear in random order if they are appearing one by one? Apologies if this is unclear or not allowed to ask.Solved420Views0likes7CommentsAlways on top
I just set myself the challenge of having elements that are "always on top". I've worked on a handful of courses in the past, that had an irregular shaped header graphic (sometimes with drop shadow), where it would have been great to be able to set some elements in the Master Template to appears always on top of other elements within the module. For those courses, I had to create a slice of the header (the irregular shaped part) and paste on each slide, where the header appeared over another element on the slide. I have had some success, and of course it is a JavaScript implementation. This is very much an ALPHA release. Here's the implementation if interested in having a play with it or extending it. I'm sure there will be some elements, that I haven't played with, that may need some extra logic in the JS. For each element you would like to appear always on top, add the string "{AOT}" in the "Alternative text" field. If the element is decorative, you can just add "{AOT}". This will be taken care of during processing (removed, and set to aria-hidden=true). If the element is non-decorative, for example an exit button, you would just add "{AOT}Exit" to the alternative text. This would also be process, and "{AOT}" removed and "Exit" retained in the ALT text. Add the following script to the "timeline starts" on the SLIDE MASTER. This ensures that the script will process on every slide. const init = () => { // inititial value for z-index let z = 999; // get all elements with data-acc-text attribute starting with "{AOT}" const elements = document.querySelectorAll('[data-acc-text^="{AOT}"]'); // loop through each element elements.forEach(element => { // get the modelId and accText from the element's dataset const { modelId, accText } = element.dataset; // get the root element with the same modelId const rootElement = document.querySelector(`[data-model-id="${modelId}"]`); // set the z-index of the root element (incrementing by 1 each time) rootElement.style.zIndex = z++; // get the alternative text by removing "{AOT}" from accText and trimming the result const alt = String(accText).replace('{AOT}', '').trim(); // get the alt element with the id "acc-${modelId}" const altElement = document.getElementById(`acc-${modelId}`); // set the alternative text to the element's dataset element.dataset.accText = alt; // re-write the inner text of the alt element altElement.innerText = alt; // if the alternative text is empty (decorative), set the aria-hidden attribute to true and the z-index to -1 if (!alt) { altElement.setAttribute('aria-hidden', 'true'); altElement.style.zIndex = '-1'; } else { if (altElement.hasAttribute('aria-label')) altElement.setAttribute('aria-label', alt); } }); }; requestAnimationFrame(() => { init(); }); I've also including a very simple example file. This just demonstrates that the designated "Always on top" elements will appear over the image on the slide.52Views2likes0Comments