I'm trying to write a userscript on Opera using Tampermonkey (although I tried ViolentMonkey already with the same results) that will run on my router's config page and calculate some values based on the statistics displayed.
The problem is, it is an .asp page, with only a frameset (no body element, although I have no idea if this is normal for asp or not, never used it) and 3 frame elements within it. After trying some DOM methods, which work but require some very inelegant approaches to actually detecting what's on the page since the url doesn't change, I stumbled upon MutationObserver which kicks ass, but I can't seem to get it to return any events, no matter what I do.
The MutationObserver works when I try it on google.com and reports normally. My code so far is just this test for MutationObserver functionality, so it's pretty much a copy/paste from here and looks like this (slightly modified):
// ==UserScript==
// #name meh
// #match http://192.168.1.1/cgi-bin/index.asp
// #run-at document-end
// ==/UserScript==
// MDN code starts here
var target = document.body;
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
console.log(mutation.type);
});
});
var config = { attributes: true, childList: true, characterData: true, subtree: true };
observer.observe(target, config);
// end of userscript
This exact code works perfectly fine on google.com. Also my #match directive isn't the problem since I log the observer to the console (not shown here) to make sure it matches the proper url.
I've tried various elements as targets (in case that was the problem) such as window.frames['framename'].document.body and the like, and I've tried with and without subtree in the configuration, as well as using document-start for the #run-at directive. No matter what, I get no mutations logged into the console.
I haven't been able to find anything online concerning this particular case so I need to ask, is there anything special about .asp pages that can mess with MutationObserver or is this something to do with frames and framesets?
edit - quite ironically, the only place online I could find to test my code other than the router's interface is The World's Worst Website. Not even jsfiddle and codepen will put up with frameset and frame.
I'm going to put this to rest. I originally wanted to approach my problem this way in order to avoid using the greasemonkey api to store values (because if I'm within a frame's context and I refresh, stored variables are wiped) and to use a more elegant and simple way of checking whether a specific frame is loaded with a specific url. Turns out browsers (Chrome, Firefox, Opera) will not spawn an event when a new url is loaded within a frame (I don't know if for iframes the behavior is the same, due to sandboxing, or iframes behave differently than ancient frames).
All in all, if you need to do something like this (maintain a variable in a userscript between refreshes of a frame), target the frame you specifically want to monitor with the #match directive and use the greasemonkey api (or whatever api your userscript extension has for storing values permanently). If you want to do this on a website you're making yourself, don't use frames (preferrably), or use postMesssage or attach event handlers where you need them.
The gist is, frames are terrible.
Related
So say I use a website very often but I don't like a certain aspect of the design, I obviously don't own the website but I don't like to have to go in every time I load a page and edit the HTML, I'd like to be able to save some HTML and every time I open this website it should replace the code automatically, or it could run some Javascript or something, or even change some of the CSS, is this possible and if so, how?
The easiest way to do something like this would be to install a userscript manager like Tampermonkey. Then you can create a userscript for the site that changes the HTML to how you want it to be, and (if you've written the code properly) it'll automatically run every time you load the site.
For example, due to a bug in Stack Exchange's CSS/Javascript, quickly double-clicking on a snippet when it's loading results in errors, so I currently have the following userscript to fix it:
// ==UserScript==
// #name Stack Snippet Modal Fixer
// #description Prevents snippet double-clicking from breaking the snippet interface
// #author CertainPerformance
// #version 1.0.0
// #include /^https://(?:(?:(?:codereview|gamedev|codegolf|meta)\.)(?:[^/]+\.)?stackexchange\.com|(?:[^/]+\.)?stackoverflow\.com)/(?:questions/(?:\d|ask/)|posts/\d+/edit|review/(?:reopen|helper|low-quality-posts|suggested-edits)(?:/\d+|$))/
// #grant none
// ==/UserScript==
document.body.appendChild(document.createElement('style')).textContent = `
.snippet-modal {
pointer-events: auto !important;
}
`;
This uses Javascript to append a <style> tag to the document, but you can make whatever other changes you want to the document as well (like changing HTML of a page, or removing style rules of an existing inline <style>, etc).
The only limits to a userscript are the limitations of Javascript on a page, but most things one would want to tweak can probably be achieved with Javascript.
Personally, I would have a hard time browsing many of the sites I frequent without the ability to write userscripts to customize sub-optimal interfaces.
You could use the browser extension Stylus, which allows you to add custom css on a per-website or on a global basis and it will load that css every time you visit any page on the specified site(s) until you turn it off.
For Chrome:
https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=en
For Firefox:
https://addons.mozilla.org/en-GB/firefox/addon/styl-us/
If you are interested in doing a little work, you can write a Google Chrome extension to do what you're asking. Take a look at https://developer.chrome.com/extensions/getstarted to get started.
I think there is already a plug in that does exactly that. I don't use it, I just remembered from years ago and find it in the Chrome Extensions store. Give it a try:
Monkey Wrench
I have a script that I want to run on EVERY page. To do it has been quite easy I simply set #include * and its done. It shows up on every page, activated by a hotkey combination I have assigned to it inside the code. It works as expected and without issues.
HOWEVER, I would like this to also be available on a blank tab as well. If you have a page with actual content (document assignment if you will) it works fine, I guess it has something to inject the script into and run with, I get that. I am wondering and hoping if there is a way to also have the script hook the blank tab page as well.
I have done considerable research on this to no avail, I am hoping some of my friends here with more extensive exposure to JS and perhaps experience gained in the trenches with regards to this matter might have a solution to offer. I would greatly appreciate it.
See the docs at "Include and exclude rules, Extra schemes". for a script to run on blank tabs, you must now explicitly set #include about:blank.
For example:
// ==UserScript==
// #name _Very noisy script
// #include about:blank
// #include *
// ==/UserScript==
alert ("Fire on blank");
However, Firefox now uses about:newtab by default, and Greasemonkey currently doesn't consider about:newtab to be "Greaseable". (It should though, and I'll look into getting a pull-request accepted for this.)
So, to get scripts firing on blank tabs, you currently must set those blank tabs back to using about:blank.
Do that by opening about:config and setting browser.newtab.url to about:blank.
I'm developing a Chrome plugin. It injects a class name to every tag.
I have some problems with webpages such as facebook in which content is loaded afterwards when you scroll down.
I'd like to know if there a way to check if new content is loaded.
By now the only solution I could find is a
setInterval(function() {
Thanks.
There is a DOMSubtreeModified event (source) that Chrome supports - see this answer for details. Your code should look something like this:
document.addEventListener('DOMSubtreeModified', function() {
$("*:not(.my_class)").addClass('my_class');
}, true);
As Konrad Dzwinel said, you can use some Mutation Event listener
document.addEventListener("DOMSubtreeModified", methodToRun);
But note that the Mutation Events are performance hogs which can't really be tamed well (they fire too often and slow down the page a lot). Therefore, they have been deprecated over a year ago and should be used only when really needed. However, they work.
If you want this for a Chrome extension, you could use the new and shiny Mutation Observers from DOM Level 4 (follow the links there, they explain a lot!). Where DOMSubtreeModified fired a thousand times, MutationObserver fires only once with all the modifications contained and accessible.
Works for (as of 2012/06):
Chrome 18+ (prefixed, window.WebKitMutationObserver)
Firefox 14+ (unprefixed)
WebKit nightlies
I just wrote a chrome extension that replaces stock symbols designted with a $ before them with data from yahoo finance. I am running into some issues though based on how twitter loads the stream. I have the js set to run on document_end but twitter loads the stream after the DOM is ready. To get around this I just checked to see if a certain Element existed and then ran the scripts if it didnt just wait 500 ms and try again.
There seems to be an issue on the search pages as well possibly because the element I am checking has a different class I did not really look into the issue yet.
The other issue is it creates a mess when there are tons of symbols in one tweet might be related to the first issue but seems like it is inserting extra DOM elements.
the project is hosted on github would be awesome to get some feedback and possibly contributions.
https://github.com/billpull/Twitter-Ticker
The easiest way is to listen to DOMMutation events. Before the browser renders the tweet, you can capture this with DOMNodeInserted event.
As Chrome 18, MutationObservers are now implemented, it is fast, and doesn't fire to quickly so it is concise. DOM Mutation Observers is asynchronous and can fire multiple changes per call! It significantly improves performance of your mutations.
An example of using MutationObservers would be:
var observer = new MutationObserver(function onMutationObserver(mutations) {
mutations.forEach(function(mutationNode) {
// New Nodes added ... Deal with it!
});
});
observer.observe(historyContainerDOM, { childList: true, subtree: true });
I have added some comments to one of my open source extensions that talk about this, feel free to take what you want.
Last week we released Omniture's analytics code onto a large volume of web sites after tinkering and testing for the last week or so.
On almost all of our site templates, it works just fine. In a few scattered, unpredictable situations, there is a crippling, browser-crashing experience that may turn away some users.
We're not able to see a relationship between the crashing templates at this time, and while there are many ways to troubleshoot, the one that's confuddling us is related to event listeners.
The sites crash when any anchor on these templates is clicked. There isn't any inline JS, and while we firebug'ed our way through the attributes of the HTML, we couldn't find a discernable loop or issue that would cause this. (while we troubleshoot, you can experience this for yourself here [warning! clicking any link in the page will cause your browser to crash!])
How do you determine if an object has a listener or not? How do you determine what will fire when event is triggered?
FYI, I'd love to set breakpoints, but
between Omnitures miserably obfuscated code and repeated browser
crashes, I'd like to research more
thoroughly how I can approach this.
I did an "inspect element" on a link in that page with firebug, and in the DOM tab it says there is an onclick function (anonymous), and also some other function called "s_onclick_0".
I coaxed firebug placing a watch like
alert(document.links[0].onclick)
to alert me the onclick function that omniture (i guess) attaches to links:
function anonymous(e) {
var s = s_c_il[0], b = s.eh(this, "onclick");
s.lnk = s.co(this);
s.t();
s.lnk = 0;
if (b) {
return this[b](e);
}
return true;
}
Maybe in the same way you can see what it is really running after all that obfuscation.
DOM doesn't provide any means to introspecting through the events listeners' collections associated with a node.
The only situation where listener can be identified is when it was added through setting a property or an attribute on the element - check on onxxx property or attribute.
There have been a talk recently on WebAPI group at W3 on whether to add this functionality. Specialists seem to be against that. I share their arguments.
A set of recommendations to the implementers of on-page analytics:
Use document-level event capturing only, this is in almost every case (besides change/submit events) sufficient
Do not execute computation-intensive code (as well as any IO operations) in the handlers, rather postpone execution with a timeout
If this two simple rules are taken into account, I bet your browser will survive
I have some experience with Omniture and looking at your s_code.js, you have several things going on in the "Link Tracking" area, for example:
/* Link Tracking Config */
s.trackDownloadLinks=true
s.trackExternalLinks=true
s.trackInlineStats=true
s.linkDownloadFileTypes="exe,zip,wav,mp3,mov,mpg,avi,wmv,pdf,doc,docx,xls,xlsx,ppt,pptx"
s.linkInternalFilters="javascript:,gatehousemedia.com"
s.linkLeaveQueryString=false
s.linkTrackVars="None"
s.linkTrackEvents="None"
I would consult with the people at Omniture and verify that your link tracking configuration is set up correctly.
Specifically, this template and the links inside seem to belong to morningsun.net and yet morningsun.net is not in the s.linkInternalFilters setting. If you are using the same s_code.js file for multiple domains, you can use javascript to set the configuration values for things like this (basing on the document.location.hostname for instance).
I don't personally have experience with the link tracking configuration or I would give you more detail on how to configure it :)
While traveling home I came to a solution that allows for introspection of event handlers on element added with AddEventListener. Run code before the inclusion of your analytics code. The code was not verified if works, but the idea, I guess is clear. It won't work in IE, however you can apply similar technique (of rewriting the API member) there as well.
(function(){
var fAddEventListener = HTMLElement.prototype.addEventListener;
HTMLElement.prototype.addEventListener = function() {
if (!this._listeners)
this._listeners = [];
this._listeners.push(arguments);
fAddEventListener.apply(this, arguments);
}
})();