This content originally appeared on DEV Community and was authored by DEV Community
The following is just some thoughts and findings I've had on creating a micro-library for personal use for TamperMonkey/Userscripts. The library is not published yet as it is still a proof of concept/learning exercise and a little messy currently
TamperMonkey Intro
Something I've recently started goofing around with is TamperMonkey, a browser extension that allows you to "install and create userscripts, which are JavaScript programs that can be used to modify web pages".
I enjoy automating and hacking things together and TamperMonkey scratches that itch for me. Browser extensions are very powerful but sometimes you just need to restyle a webpage or inject a little bit of JS. A few scripts I've created recently that I found myself using daily are:
- Display crime stats for properties on Rightmove (i've been looking at houses a lot lately)
- Add a hyperlink on every GitHub PR to a "short lived environment" & a link to JIRA tickets
- Autopopulating web forms for misc dev tools from a GitHub PR
- Enrich websites I'm developing with detailed debugging information (e.g Session helpers, visible commit_ref, links to logging for the logged in user, cloud tools, links to CMS and CRM entries, envconfigs at a glance). Can be very useful when testers, product owners, etc are running into issues on dev environments.
Why Create A Library Though?
I find myself constantly copy/pasting boilerplate code for new scripts or re-writing from memory and introducing bugs. Want to insert an element? You'll need a lot of lines of code for this:
var textEl = document.createElement('p')
textEl.innerText = "foo";
textEl.className = "";
textEl.onclick = populate;
logo.appendChild(textEl);
// or
someOtherElement.parentNode.appendChild();
// or this monstrosity
textEl.insertBefore(someOtherElement, textEl.firstChild);
// or some other ways
And the same applies for making fetch requests. fetch
blocks most requests due to CORS. So you'll need to use GM_xmlhttpRequest
instead. But that doesn't use promises because xmlhttpRequest is ancient. So you'll probably want to wrap in a Promise. Then you'll need to parse the response. Then some other stuff. I've needed to copy/paste or change the following for multiple scripts now:
// The following header is required to make cross origin requests:
// @grant GM_xmlhttpRequest
async function parseWebsite(postcode) {
const url = `https://www.ilivehere.co.uk/check-my-postcode/${postcode}`;
const response = await Request(url); // Request is defined below
const html = response.responseText;
var parser = new DOMParser();
// Parse the text
var doc = parser.parseFromString(html, "text/html");
const number = doc.querySelector('.ilivehere-rating-number').textContent;
return number;
}
// Wrap xmlRequest in a Promise
function Request(url, opt={}) {
Object.assign(opt, {
url,
timeout: 2000,
responseType: 'json'
})
return new Promise((resolve, reject) => {
opt.onerror = opt.ontimeout = reject
opt.onload = resolve
GM_xmlhttpRequest(opt)
})
}
Another example - reading a cookie. Unfortunately there's no document.readCookie("foo")
so I find myself copy/pasting this and other 'utility' functions across Userscripts:
const getCookie = (name)=> {
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) return parts.pop().split(';').shift();
}
HamsterScript Intro
Assuming you've made it this far, thanks for sticking around. I decided to create HamsterScript for two reasons:
- Make it slightly easier to create future Userscripts by using a library with a set of easy to use, common helper functions
- Create a "cheatsheet" for personal Userscript knowledge
HamsterScript Usage
Installing a library is really simple in a Userscript. Heck, you could even include Vue or jQuery if you wanted too!
// @name [HamsterScript Example]
...
// @grant GM_xmlhttpRequest
// @require <placeholder>/hamsterscript-latest.min.js
...
(async function() {
'use strict';
const doc = await hamster.fetchDom("<some-url>");
const stats = doc.querySelector('.body-band-highlight').childNodes[1].textContent;
const newBtn = hamster.insert({tag: "p", text}, `[itemprop="streetAddress"]`);
})
Only 3 lines of code for my most common use case. The same in vanilla JS is 20+ lines. Understandably you may lose some readability (what does hamster.fetchDom actually do?). Well as I previously mentioned, the goal of this library is to create a cheatsheet and abstract common functions into a reusable library! You can see what hamster.fetchDom does in the HamsterScript docs OR on GitHub, along with a full explanation and alternative methods
Findings
I doubt anybody will ever come across or use this framework but it's been a fun little exercise for a few reasons:
- Learnt JSDoc and generating documentation
- Found out about multiple TamperMonkey APIs and other scripts like
waitForKeyElements
- Comparing the "before" and "after" of using HamsterScript (in one case, 95 lines was simplified to 55 lines)
- Creating pipelines from scratch to minify and deploy my library
Future Steps
- Think about versioning my library
- Hosting multiple versions on a CDN along with multiple versions of documentation
- Prevent breaking changes
This content originally appeared on DEV Community and was authored by DEV Community
DEV Community | Sciencx (2022-03-14T01:29:40+00:00) HamsterScript – a micro-library for TamperMonkey. Retrieved from https://www.scien.cx/2022/03/14/hamsterscript-a-micro-library-for-tampermonkey/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.