A Weird Imagination

User scripts on iPad

The problem#

Google Forms is a tool that allows for easily setting up simple structured data entry. But it's designed to make it easy to analyze a lot of data that has been entered, not to view a single entry. There is a view to show individual entries, but it's very cluttered due to including all of the options that were not selected as well as those that were selected. A display that showed only the entries that were selected could be used as a quick and dirty way to make a form letter-like website.

To make this problem harder, the solution has to run on iPad, a platform not exactly known for its user programmability.

The solution#

Bookmark this link: hide unselected items in Google Form. Then select that bookmark when on the appropriate Google Forms page. Note that in addition to hiding unselected entries, if the entry that is not select has a value of "Yes", then its entire section will be hidden. If you don't want that behavior, bookmark this variant of the script instead.

The details#

Customizing webpages#

Let's back up a bit.

The web is an interesting platform because most pages are the combination of multiple files that don't have to directly reference each other to work together. This means that users can change the way a web page look for them by defining their own user style. We could use user styles to inject some CSS that would tell the browser to hide the elements that we don't want to see. Similarly, instead of using CSS, we could also write a short JavaScript snippet that would run as a user script to hide those elements.

Unfortunately, both of those options require web browser extensions which are not available on mobile browsers, so they are not an appropriate solution for running on an iPad. Luckily, there's a workaround: bookmarklets. Bookmarklets are JavaScript programs stored in bookmarks using the javascript: URI scheme. When the bookmark is selected, instead of going to a page, it executes the JavaScript program with the current page's variables available to it. They're not as powerful as user scripts because they have to be activated manually, but they can accomplish many of the same tasks.

Writing the script#

That answers how to run our custom JavaScript, but not how I came up with that specific script.

I knew I wanted to hide a set of elements on the page, so the script would look something like

document.querySelectorAll(selector)
        .forEach(el => el.style.display = 'none');

which just leaves determining the right value for selector.

Most modern desktop browsers have an "Inspect" feature where you can right-click on an element and it will bring up the HTML of that element in context. Additionally, when hovering over parts of the HTML, it will highlight which part of the page it corresponds to. Using that, it's fairly straightforward to identify which HTML element is the one we want to hide.

As Google Forms isn't trying to obfuscate what the elements are for, their class names are straightforward: disabled items have the isDisabled class, checked items have the isChecked class (well, strangely, so do disabled items), so with a little experimentation, we can see that the CSS selector .isDisabled:not(.isChecked) selects the disabled items which are not checked.

Additionally, the form I was doing this for had some sections that were "Yes"/"No" choices instead of multiple-choice, so selecting "No" meant the whole section should be hidden. I accomplished that by when the "Yes" was to be hidden (determined by checking el.getAttribute('data-value') == 'Yes' as I noticed the data-value attribute when looking at the HTML), instead hiding the ancestor of it that was the element for the entire section. I determined which ancestor simply by repeating .parentNode and re-running the script in the JavaScript console until it did what I expected.

Putting it all together, here's the final script:

document.querySelectorAll('.isDisabled:not(.isChecked)')
        .forEach(el =>
            (el.getAttribute('data-value') == 'Yes'
                ? el.parentNode.parentNode.parentNode
                    .parentNode.parentNode.parentNode
                    .parentNode.parentNode
                : el)
            .style.display = 'none');

Making the bookmarklet#

Now that we have our script, we can convert it into a bookmarklet. A URI is a single line of text, and wrapping the code in an anonymous function prevents variable name collisions. I used this encoder which does both for you. I also found another blog post of useful tips for building bookmarklets, including pointing out that if you're worried about the size of your bookmarklets, you can make your bookmarklet actually just be a small reference to an external JavaScript file that hosted on a web server somewhere.

Comments

Have something to add? Post a comment by sending an email to comments@aweirdimagination.net. You may use Markdown for formatting.

There are no comments yet.