Goal: I'm trying to add an input element on Frontend Mentor to sort/search solutions.
Problem: The input element appears on the page but disappears after a second.
Code:
// ==UserScript==
// #name FEM
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author bunee
// #match https://www.frontendmentor.io/solutions
// #icon https://www.google.com/s2/favicons?domain=frontendmentor.io
// #grant none
// ==/UserScript==
(function() {
'use strict';
window.addEventListener("load", () => {
const inputElement = document.createElement("INPUT");
inputElement.setAttribute("type", "text");
const navBar = document.querySelector(".NavigationSecondary__Wrapper-sc-13y3yjk-0>.wide-container");
navBar.append(inputElement);
console.log(inputElement);
}, false);
})();
Here's where I'm trying to add it.
If you can, Please run this script and let me know how I'm wrong.
Seems like some dynamic shenanigangs on the website delete your addition when it happens too early.
Have you tried just delaying it with a timeout?
Something like
// ==UserScript==
// #name FEM
// #namespace http://tampermonkey.net/
// #version 0.1
// #description try to take over the world!
// #author bunee
// #match https://www.frontendmentor.io/solutions
// #icon https://www.google.com/s2/favicons?domain=frontendmentor.io
// #grant none
// ==/UserScript==
setTimeout(function(){
const inputElement = document.createElement("INPUT");
inputElement.setAttribute("type", "text");
const navBar = document.querySelector(".NavigationSecondary__Wrapper-sc-13y3yjk-0>.wide-container");
//navBar.append(inputElement);
navBar.insertBefore(inputElement, navBar.lastChild);
console.log(inputElement);
}, 2000);
just adapt it as needed.
P.S.
navBar.append(inputElement) adds your new element to the end while
navBar.insertBefore(inputElement, navBar.lastChild); adds it in the middle as per your question.
This script works just fine in Tampermonkey on both Chrome and Firefox, but doesn't work in Greasemonkey on Firefox. When I say it doesn't work I mean the button does not show up on the page. I'm using Chrome 61.0.3163.79 and Firefox 52.3.0.
The firefox console shows this warning but I'm not sure if it's relevant:
unreachable code after return statement[Learn More] jquery-1.3.2.min.js
Here's the code. Using greasemonkey on Firefox, I see "create button" in the console, but not "button has been created", which makes me think something is going wrong with the button creation. Any help greatly appreciated!
// ==UserScript==
// #name MyScript
// #namespace MyNS
// #description Adds a button to insert text into 2 text boxes
// #version 0.1
// #include *Removed for confidentiality*
// #compatible Greasemonkey
// ==/UserScript==
var descriptionText = "myDescription";
var testingText = "myTesting";
// Check if jQuery's loaded
function GM_wait() {
if (typeof unsafeWindow.jQuery == 'undefined') { window.setTimeout(GM_wait,100); }
else { $ = unsafeWindow.jQuery; init(); }
}
// All your GM code must be inside this function
function init() {
var description = $('#description');
var testingDone = $('#testing_done');
function insertText(template, editor) {
if (template) {
if (!editor._editing) {
editor.startEdit();
}
editor._field.queue(function() {
var oldVal = editor._field.val();
editor._field.val(template + (oldVal ? "\n" : "") + oldVal);
editor._field.keyup();
editor._field.dequeue();
});
}
}
function createButton(editor) {
console.log("create button:");
editor._insertTextButton = $('<input/>')
.attr('type', 'button')
.attr('value', 'Insert CR Template')
.css('margin-left', '1em')
.click(function() {
insertText(testingText, testingDoneEditor);
insertText(descriptionText, descriptionEditor);
});
console.log("button has been created");
editor._insertTextButton.insertAfter(editor._editIcon);
}
descriptionEditor = description.data('inlineEditor');
testingDoneEditor = testingDone.data('inlineEditor');
createButton(descriptionEditor);
}
GM_wait();
This question already has answers here:
Changing Javascript on an HTML page out of my control [duplicate]
(2 answers)
Closed 4 years ago.
There's a website I use written in some mighty fine javascript. Hardly any globals, closures everywhere and it uses strict mode. This is making it really hard to inject my own functionality into the website.
The website client objects are initialised in a jQuery.ready() call:
$(window).ready(function () {
var a, b, c, d;
// Setup global data [...]
// Setup configuration [...]
a = GlobalFoo.ConstructorA();
b = GlobalFoo.ConstructorB(a);
// Really wish I could put stuff here
c = GlobalFoo.ConstructorC(a, b);
d = GlobalFoo.ConstructorD(b, c);
// etc.
});
How can I, for example, replace b.someMethod() with my own code before the other constructors are called?
Can I stop the ready event from happening or replace it with my own code? Since it's quite small I can just duplicate a modified version in my code.
After a bit more searching I found this wonderful page by dindog. There is also this page on the GreaseSpot wiki describing #run-at.
#run-at allows your userscript to run before all other code. The beforescriptexecute event allows you to check each script before it executes. You can then skip or modify it.
My final solution is:
// ==UserScript==
// #name ...
// #description ...
// #namespace ...
// #include ...
// #version 1
// #run-at document-start
// #grant none
// ==/UserScript==
(function (w) {
// Remove the current jQuery ready code
function pre_script_execute_handler (e) {
var target = e.target;
if (target.innerHTML.indexOf("$(window).ready(") != -1) {
console.log('Removed ready');
e.preventDefault();
e.stopPropagation();
addReady();
}
}
w.addEventListener('beforescriptexecute', pre_script_execute_handler);
// Add new jQuery ready code
function addReady () {
console.log('Adding new ready');
w.$(window).ready(function () {
console.log('Our ready called');
});
}
}) (unsafeWindow);
My WinJS app uses the single navigation model. There is some common code that I would like to apply to every page in the app. Instead of placing the code in each page's ready function, I would like to be able to able to define a "global" ready function that will be executed when a page's ready event is fired. Any ideas?
you can define a Mixin object with utility function used for all pages.
utils.js:
PageMixin = {
ready: function ready(element, options)
{
this.element = element;
this.options = options;
this.initialize();
this.onready();
},
initialize: function initialize()
{
// write common initialize code here
}
};
page.js:
var Page = WinJS.UI.Pages.define('/pages/mypage/page.html',
{
onready: function onready()
{
// page specific initialization code here
}
});
// this will make all PageMixin util methods available on Page.
WinJS.Class.mix(Page, PageMixin);
refer WinJS.Class.mixin for details.
Risking of asking a stupid question I will give it a try though.
Is it possible to display a progress indicator while RequireJs is loading dependencies?
For example:
require(['jquery'], function($) {
// Well, jQuery loaded in quite some time due to a low-speed connection
//
// Or maybe I wanted to show an overlay to prevent user of clicking on UI widgets until the load is complete
});
I don't want to start modifying the RequireJS source if there's some plugin out there which didn't show up in my Google searches.
Thanks for all your help.
It is possible to display a spinner or progress indicator, but not the bar itself. I could get status of requirejs (currently loading or idle), but not the % of loaded/needed to be loaded modules, because their dependencies are parsed upon every module load, but not at the beginning.
But, anyway, page with, at least, a simple spinner, is much better, than just blank page, while user waits.
No changes to requirejs needed! So..
Assume we have file require.conf.js with
require.config({...})
require(["app"], function () {
// main entry point to your hugde app's code
});
and it is loaded by html using
<script data-main="require.conf" type="text/javascript" src="bower_components/requirejs/require.js"></script>
This is standard requirejs scenario. Let's add the indicator to the
<div id="requirejs-loading-panel">
<div id="requirejs-loading-status"></div>
<div id="requirejs-loading-module-name"></div>
</div>
Ok, let's catch up requirejs's function called require.onResourceLoad and do all the magic needed. It will be called by requirejs upon every module load, passing the requirejs's context with dep tree and all other staff. We will use context to find out, whether requirejs is loading something or not. I did it in scheduled timer call, because onResourceLoad() is called only while loading, not when it is done loading. This code needs to be added to require.js.conf:
require.onResourceLoad = function (context, map, depMaps) {
var loadingStatusEl = document.getElementById('requirejs-loading-status'),
loadingModuleNameEl = document.getElementById('requirejs-loading-module-name');
var panel = document.getElementById('requirejs-loading-panel');
if (loadingStatusEl && loadingModuleNameEl) {
if (!context) { // we well call onResourceLoad(false) by ourselves when requirejs is not loading anything => hide the indicator and exit
panel.style.display = "none";
return;
}
panel.style.display = ""; // show indicator when any module is loaded and shedule requirejs status (loading/idle) check
clearTimeout(panel.ttimer);
panel.ttimer = setTimeout(function () {
var context = require.s.contexts._;
var inited = true;
for (name in context.registry) {
var m = context.registry[name];
if (m.inited !== true) {
inited = false;
break;
}
} // here the "inited" variable will be true, if requirejs is "idle", false if "loading"
if (inited) {
require.onResourceLoad(false); // will fire if module loads in 400ms. TODO: reset this timer for slow module loading
}
}, 400)
if (map && map.name) { // we will add one dot ('.') and a currently loaded module name to the indicator
loadingStatusEl.innerHTML = loadingStatusEl.innerHTML += '.'; //add one more dot character
loadingModuleNameEl.innerHTML = map.name + (map.url ? ' at ' + map.url : '');
}
} else {
}
};
One problem is: we cannot somehow figure out how much modules are needed to load, so we can't compute the actual % of loading progress. But, at least, we can find out, whether we're loading something or not (and event get currently loading module name) and show it to a nervous user
Since v. 2.1.19 RequireJS has onNodeCreated config property. If you assign a function to it, that function will be called each time a <script> element is appended to a document to load a module.
The function will be provided with node, config, moduleName, and url arguments. By attaching event listeners to the node, you can detect when the script has been loaded or failed to be loaded.
Now you can detect when loading process starts and stops and can use that information to notify users:
require.config({
/* ... paths, shims, etc. */
onNodeCreated: function(node, config, moduleName, url) {
console.log('module ' + moduleName + ' is about to be loaded');
node.addEventListener('load', function() {
console.log('module ' + moduleName + ' has been loaded');
});
node.addEventListener('error', function() {
console.log('module ' + moduleName + ' could not be loaded');
});
},
});
For IE < 9 you'd need additional code to attach event listeners.
Note: this only works for modules requested by RequireJS directly. Plugins (requirejs/text and such) each use their own loading mechanism and do not trigger onNodeCreated. Some trigger onXhr and onXhrComplete though, so you can handle them too:
require.config({
/* ... paths, shims, etc. */
config: {
text: {
onXhr: function(xhr, url) {
console.log('file ' + url + ' is about to be loaded');
},
onXhrComplete: function(xhr, url) {
console.log('file ' + url + ' has been loaded');
}
}
}
});
No, this isn't possible. RequrieJs loads the module by creating a new script tag in the DOM and listens to the load event. There are no update events in between start and end loading. So it's not a limitation of requireJs but the DOM.