NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Prefill MS Forms // @namespace http://tampermonkey.net/ // @version 0.8.2 // @description Pre-populate fields in Microsoft Forms via URL parameter // @author KyleMit // @copyright 2018, KyleMit // @license MIT // @match https://forms.office.com/Pages/ResponsePage.aspx* // @grant none // @homepage https://github.com/KyleMit/CustomizeTheWeb#readme // @supportURL https://github.com/KyleMit/CustomizeTheWeb/issues // ==/UserScript== // ==OpenUserJS== // @author KyleMit // ==/OpenUserJS== /* global URLSearchParams */ (function() { 'use strict'; // prior art // [Pre-populate fields in Microsoft Forms via URL parameter?](https://techcommunity.microsoft.com/t5/microsoft-forms/p/m-p/113803) // [Allow pre-populated data via URL parameter](https://microsoftforms.uservoice.com/forums/386451/suggestions/18741331) // sample form // https://forms.office.com/Pages/ResponsePage.aspx?id=O5O0IK26PEOcAnDtzHVZxoUFQnFGPs9NnXgmxPQfHEFUQVBRUTQzRjc0OVNXM1VYSklHTlk3MkhMNi4u // 1. handle page load // parse query param // apply data to forms // 2. handle form change // extract data // update URL // let formTypes = [...document.querySelectorAll('[id$="-questiontype"]')].map(el => ({id: el.id, text: el.textContent})) let questionTypes = [{ type: "Single line text", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Single line text" }, getValue: (el) => { return el.querySelector("input.office-form-question-textbox").value }, setValue: (el, val) => { el.querySelector("input.office-form-question-textbox").value = val } }, { type: "Multi line text", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Multi Line Text" }, getValue: (el) => { return el.querySelector("textarea.office-form-question-textbox").value }, setValue: (el, val) => { el.querySelector("textarea.office-form-question-textbox").value = val } }, { type: "Date", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Date" }, getValue: (el) => { return el.querySelector("input.office-form-question-textbox").value }, setValue: (el, val) => { el.querySelector("input.office-form-question-textbox").value = val } }, { type: "Single choice (select)", matches: (el) => { return el.querySelector(".select-placeholder") }, getValue: (el) => { return el.querySelector(".select-placeholder-text").textContent }, setValue: (el, val) => { // get fake select let sel = el.querySelector("select-placeholder") // open select sel.click(); // give it a second setTimeout(function() { // get fake dropdown options let options = [...el.querySelectorAll(".select-option-menu-container li")] // get new option let newOption = options.find(opt => opt.querySelector(".select-option-content").textContent == val) // select new option newOption.click() }, 30) } }, { type: "Single choice (radio)", matches: (el) => { return el.querySelector(".office-form-question-choice .radio") }, getValue: (el) => { let option = [...el.querySelectorAll("input[type='radio']")].find(o => o.checked) return option ? option.value : null }, setValue: (el, val) => { let options = [...el.querySelectorAll("input[type='radio']")] // get new option let newOption = options.find(opt => opt.value == val) // set new option newOption.checked = true } }, { type: "Multiple choice", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Multiple choice" }, getValue: (el) => { return [...el.querySelectorAll("input[type='checkbox']")].filter(o => o.checked).map(o => o.value) }, setValue: (el, val) => { let options = [...el.querySelectorAll("input[type='checkbox']")] // get new option let selOptions = options.filter(opt => val.includes(opt.value)) // set new option selOptions.forEach(o => { o.checked = true }) } }, { type: "Rating", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Rating" }, getValue: (el) => { return null }, setValue: (el, val) => { // todo // // menu must be visible for events to fire https://stackoverflow.com/a/33971082/1366033 // let range = el.querySelector(".rateit-range") // // only listens for mousedown event (not click) https://stackoverflow.com/a/20567978/1366033 // range.dispatchEvent(new Event('mouseup')); // // menu must be visible for events to fire https://stackoverflow.com/a/33971082/1366033 // let menu = el.querySelector(".rateit-hover") // menu.style.display = "flex" // let opts = [...el.querySelectorAll(".rateit-slected span")] // let opt = opts[val - 1] // // only listens for mousedown event (not click) https://stackoverflow.com/a/20567978/1366033 // opt.dispatchEvent(new Event('mouseup')); } }, { type: "Ranking", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Ranking" }, getValue: (el) => { return null }, setValue: (el, val) => { // todo } }, { type: "Likert", matches: (el) => { return el.querySelector('[id$="-questiontype"]').textContent == "Likert" }, getValue: (el) => { return null }, setValue: (el, val) => { // todo } }, ] let getQuestionNum = (el) => el.querySelector(".ordinal-number").textContent.replace(".", "") let getQuestionType = (el) => questionTypes.find(q => q.matches(el)) let origUrl = window.location.pathname + window.location.search let updateUrlWithData = () => { // extract values let questions = [...document.querySelectorAll('.office-form-question')] let formDataEntries = questions.map(el => { let num = getQuestionNum(el) // get question type (determines how to extract value) let type = getQuestionType(el) let key = 'q' + num let value = type.getValue(el) return [key, value] }) let formData = Object.fromEntries(formDataEntries.filter(ent => ent[1])) let queryParams = new URLSearchParams(formData).toString() let newUrl = `${origUrl}&${queryParams}` history.pushState({}, '', newUrl) } let updateDataWithUrl = () => { // get values from URL let urlData = Object.fromEntries(new URLSearchParams(location.search)); // apply values for each question let questions = [...document.querySelectorAll('.office-form-question')] questions.forEach(el => { let num = getQuestionNum(el) // get question type (determines how to extract value) let type = getQuestionType(el) // get question value let key = 'q' + num let value = urlData[key] if (value) { type.setValue(el, value) } }) } // handle page load event and parse data from URL into form // https://stackoverflow.com/a/36096571/1366033 document.addEventListener("DOMContentLoaded", function() {}) window.addEventListener("load", updateDataWithUrl); // handle form change events and push data to URL document.addEventListener('change', updateUrlWithData) })();