After some googling and looking at tutorials, I have code along these lines:
File: background.js:
chrome.tabs.onUpdated.addListener( function (tabId, changeInfo, tab) {
if (changeInfo.status == 'complete' && tab.active) {
chrome.tabs.executeScript(tab.ib, {
file: 'inject.js'
});
}
})
File: inject.js
(function() {
function remove_by_class(className) {
var elem = document.getElementsByClassName(className)[0];
elem.parentNode.removeChild(elem);
}
function remove_by_title(itemTitle) {
var elem = document.querySelector('[title = itemTitle]');
elem.parentNode.removeChild(elem);
}
if (url.includes('SOME_URL_SUBSTRING')){
remove_by_class('CLASS_NAME');
remove_by_title('ITEM_TITLE');
}
})();
This removes certain elements, usually a second or two after I see them load in. And if I understand correctly, it can only remove elements from the page as it 'initially' exists, when it is first loaded - elements created later (by future Javascript actions on the page) are unaffected, because my extension's code is only injected & executed once.
What I'm looking for instead is some sort of 'always-on' extension that proactively watches for the loading of certain elements. Basically, I want to have a function which is called every time an HTML element is loaded/created in the page, and only allow the element to actually be placed if the function returns 'true'.
What is the easiest way to accomplish something like this?
EDIT: as an example, say I wanted to block the YouTube logo (class name style-scope ytd-topbar-logo-renderer from loading). I guess I would like a MWE that stops it from loading.
(For context: I am completely new to both Chrome extensions in particular and Javascript in general, but otherwise somewhat familiar with programming. I am mostly just curious/playing around right now, but there is a vague goal of making a kind of 'productivity tool' for myself, allowing me to use Facebook, Youtube etc for exactly what I need them for, with distracting or extraneous (to me) elements, such as Recommended Videos, redacted.)
Related
How can I remove script elements before they are being executed?
I thought about using the DOMNodeInserted event, but apparently it doesn't catch script elements. I've also tried using the jQuery livequery plugin like that:
$("script").livequery(function () {
$(this).remove();
});
It did remove the script element, but after it was executed.
I'm looking for a cross-browser solution, but I'm not even sure if that's possible. I read about Mutation Observers which seems close enough but I'm not sure if it can solve my problem.
It would be even better if there was a way to modify the script content before it is being executed without removing and recreating it.
Removing a script element does not do anything. If you can somehow access a script element, it was executed a long time ago and removing it will have no effect.
So we need to work around it. If your script element is at the top of the page like this:
<head>
<script src="yourscript.js"></script>
You could make a synchronous ajax request to the same page, so you can parse its content into a new document, modify all script tags and then replace
the current document with the modified document.
var xhr = new XMLHttpRequest,
content,
doc,
scripts;
xhr.open( "GET", document.URL, false );
xhr.send(null);
content = xhr.responseText;
doc = document.implementation.createHTMLDocument(""+(document.title || ""));
doc.open();
doc.write(content);
doc.close();
scripts = doc.getElementsByTagName("script");
//Modify scripts as you please
[].forEach.call( scripts, function( script ) {
script.removeAttribute("src");
script.innerHTML = 'alert("hello world");';
});
//Doing this will activate all the modified scripts and the "old page" will be gone as the document is replaced
document.replaceChild( document.importNode(doc.documentElement, true), document.documentElement);
Unfortunately this cannot be set up in jsfiddle or jsbin. But you should be able to copy paste this code exactly as it is into this
page's console in google chrome. You should see the alerts and when you inspect the live dom, each script was modified.
The difference is that we are running this after scripts have been executed on the page, so the old scripts should still have a working effect on the page.
That's why, for this to work, you need to be the very first script on the page to do it.
Tested to work in google chrome. Firefox is completely ignoring the doc.write call for some reason.
i donot know what you are trying to do. But it is better to load them on request rather than delete on some conditions.
$.getScript('helloworld.js', function() {
$("#content").html('
Javascript is loaded successful!
');
});
If you wants to remove scripts before there execution, its not possible.
But what you can do is, remove script programatically on a condition & if have an issue with memory-leaks, then you can call below code before remove script.
var var1 = 'hello';
var cleanAll = function () {
delete window.var1;
delete window.cleanAll;
};
// unload all resources
cleanAll();
I am making a web page reader that needs to inventory the text nodes in the document when it commences reading, because it reads each sentence down the page. So I'm "crawling" the text nodes you could say.
I have a procedure that uses document.createTreeWalker to take that inventory of text nodes.
I haven't figured out the pattern (I think there is one), but at one point when I use document.body, the document that gets pointed to is not the main page, but the document of an iframe. In my current debugging this happens to be a twitter widget, but I suppose it could be anything. This isn't a twitter question, but you can let that inform your answer if you happen to know twitter is doing something extra-ordinary to make document always go to it instead of the top document. In any case, regardless of the source, I need to get the right document.
What do I mean by the right document, you ask me? I'd say the document hosting the selected text, or if no text is selected then the top document.
But my real question is how did this happen, why is this happening? The last time I messed around with the dom was in 2009 when I wrote a web page reader in IE. Times have changed; I'm writing a Chrome extension and web pages seem 1000x more complex these days. Honestly, it's like a circus on the average web page, and most of it you don't see; it's buried beneath and lurking to trip up any robot like my reader.
I don't want to make a hard coded-rule for twitter, or any other widget. There must be a thousand such things that can end up adding / injecting themselves into the page. I literally can't get into the business of custom rules.
this.LoadAllTextNodes = function () {
this.AllTextNodes = textNodesUnder(document.body); // at some point, this document starts referring to something other than the top document. How did the definition of "document" change?
}
function textNodesUnder(root) {
var textNodes = [];
if (root.nodeType == 3)
textNodes.push(root);
else {
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, getTextElements, false);
var node;
while (node = treeWalker.nextNode())
textNodes.push(node);
}
return textNodes; // Array
}
function getTextElements(node) {
if (['SCRIPT', 'STYLE'].indexOf(node.parentNode.tagName) !== 0 && node.nodeValue !== '') //filter out script elements and empty elements
return NodeFilter.FILTER_ACCEPT
else
return NodeFilter.FILTER_SKIP
}
The web page I'm testing on happens to be https://code.visualstudio.com/blogs/2016/02/23/introducing-chrome-debugger-for-vs-code. The fact that the topic matter of the page deals with Chrome debugging is just a coincidence. It has no relation to the question. I'm just adding it in the event you'd like to see the source of the page.
<iframe id="twitter-widget-0" scrolling="no" frameborder="0" allowtransparency="true" class="twitter-follow-button twitter-follow-button-rendered" title="Twitter Follow Button" src="https://platform.twitter.com/widgets/follow_button.d59f1863bc12f58215682d9908af95aa.en.html#dnt=false&id=twitter-widget-0&lang=en&screen_name=code&show_count=true&show_screen_name=true&size=m&time=1474137195557" style="position: static; visibility: visible; width: 191px; height: 20px;" data-screen-name="code"></iframe>
In a chrome extension, the content scripts get run for each and every window, including the top window and all iframes. In this way, Chrome extension access trumps cross-site restrictions which a script running in a script tag may have.
This was instantiating a context for each frame which pointed the duplications of my extension code running in that frame to their respective documents, not the top window's document.
It runs the code in parallel. In my case each frame was queuing up content to be read without me knowing it, for the singleton window.speechSynthesis to read.
The fix was simple; just don't run in non-top windows:
if (window != window.top) return; // don't run in frames
We are trying to build an HTML game in which abobe edge is being used for animations and those animations are being inserted into iframes. We are trying to preload the iframes before removing the 'loading screen' so that the users won't see blank iframes initially.
Here is the code for loading the iframes.
We have a global variable var middleBotLoaded = false;
The following function tries to dynamically populate the iframe and once the iframe has loaded , we are assigning the variable to true
function _trackIFrameLoading()
{
if (document.getElementById("botzmiddleidlesequence_iframe").attachEvent)
{
document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { middleBotLoaded = true; });
}
else
{
document.getElementById("botzmiddleidlesequence_iframe").onload = function() { middleBotLoaded = true; };
}
document.getElementById("botzmiddleidlesequence_iframe").src = APP_BASE_URL + "blitzbotzidlesequence/blitzbotz/"+middleBotzId;
}
We have a method to check if the global variable has become true and if so , we are removing the loading screen.The method is being called in a interval of 500 milliseconds
setTimeout(_haveAllCharactersLoaded,500);
function _haveAllCharactersLoaded()
{
if(middleBotLoaded == true)
{
$(jOverlay).fadeOut(800, function() {
$(jOverlay).remove();
});
}
else
{
setTimeout(_haveAllCharactersLoaded,500);
}
}
The problem is that even after the loading screen disappears , the iframe contents take time to come up on the screen .
We have observed that the duration depends on the speed of the net connection , but then , isn't using onload the whole point of making sure that the contents have loaded.
Is there any other approach for dealing with this problem.
Thanks.
EDIT : I have to wait for two days before I can start a bounty but I am willing to award it to anyone who can provide a canonical answer to the question.
I have two answers here.
First, I think you should reconsider the way you're coding this game, unless it's a static, turn based game that only relies upon animations (think Pokemon.)
Second, I have a suggestion for you to try in fixing your code.
First Answer:
You asked if there is any other approach to dealing with this problem.
My first reaction to that, would be to skip using iFrames entirely. Adobe Edge may provide you with a good way to craft animations, but for use in a game engine you will only find yourself fighting against the design of how Adobe Edge handles it's animations.
Instead, I would recommend learning how to use HTML5's canvas element. Canvas is built to handle dynamically loaded content (such as your game engine will be generating.) I can only imagine the event of having particle effect animation overlayed onto a game character as he is hit by a weapon. With your current approach, would you place that in an iFrame? Then, how would you ensure that this particle effect is placed on the correct location on the object?
There are many resources out there to help you begin learning the code you need to make a true game engine in the browser. I would recommend beginning by learning how Canvas works. If you want to animate using the DOM, learn about requestAnimationFrame.
http://creativejs.com/
http://www.paulirish.com/2011/requestanimationframe-for-smart-animating/
https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Canvas_tutorial
Second Answer:
I would recommend looking into the variable scope of your middleBotLoaded. This answer (Set variable in parent window from iframe) would be a good place to look.
Instead of using document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { middleBotLoaded = true; });
try using document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { parent.middleBotLoaded = true; });
Alternatively, try something along these lines:
onLoad event:
document.getElementById("botzmiddleidlesequence_iframe").attachEvent("onload", function() { parent.middleBotLoaded();});
Function to handle loading:
function middleBotLoaded()
{
$(jOverlay).fadeOut(800, function() {
$(jOverlay).remove();
});
}
It's a better practice to directly call an event, rather than polling for variable changes using setTimeout.
Photo sets in Tumblr are served up as iFrames.
I want to manipulate that iframe via jQuery to look the way i want it too, which I'm able to do, but it doesn't always finish what it's doing.
I think it's because there is a lot of things happening concurrently as the page loads. I need a way to check that this iFrame, which I have no control over, loads 100% before i do the fun stuff.
There are various techniques suggested in other questions about this, but I don't think they are right for this situation. Here's one example from the Stack Overflow question "jQuery .ready in a dynamically inserted iframe"
function callIframe(url, callback) {
$(document.body).append('<IFRAME id="myId" ...>');
$('iframe#myId').attr('src', url);
$('iframe#myId').load(function()
{
callback(this);
});
}
But that makes no sense to me whatsoever to me, even though it got ticked and had 100 upvotes.
In this 'answer' the iframe is being appended by the function. That to me is not a dynamically loaded iframe!
Here is my code with the 'dumb' iframe load checker. The code works, but only sometimes. You can see it in action here http://syndex.me (third post is a photoset)
$(".post_relative").each(function () {
var post_relative = $(this);
var photoset = post_relative.find('.html_photoset');
if(photoset.length){
var myFrame = photoset.find("iframe");
myFrame.load(function(){
var newCover=myFrame.contents().find('.photoset').children(':first-child').children(':first-child').attr("href")+ '?'+(Math.random()*10000);
post_relative.append("<img src='"+ newCover +"' alt='Cover Page' />");
var psHeight = post_relative.find('img');
psHeight.load(function(){
if (psHeight.height()>heightRef){
psHeight.height(heightRef);
}
myFrame.hide();
})
})
}
});
So the question is: How do you execute a script based on the 100% loading and readiness of an iframe?
You have write data outside the context of each frame using the onload event. You have two options:
cookies
localStorage
The second is a vastly superior solution, but it is not supported on IE7. If it is in the same schema, domain, and port then you can access these items from both frames.
If your using jquery have you tried
$("#iFrameId").load(function (){
// do something once the iframe is loaded
});
I have a problem.
A strange problem.
I have this part of code:
Actions.loadWizzard = function(href)
{
alert(1);
var wizardTimer;
var wizardTimer2;
if (navigationObject.getLocation(href) === "ProductInformationWizzard") {
navigationObject.newPage("loading");
wizardTimer = setTimeout("navigationObject.newPage('contentProductInformationWizzard');", 3000);
wizardTimer2 = setTimeout("window.productInformationWizzardObject.init()", 1000);
} else if (navigationObject.getLocation(href) === "contentAdviceWizzard") {
navigationObject.newPage("loading");
wizardTimer2 = setTimeout("window.adviceWizzardObject.init()", 10000);
}
return;
};
And on the normal browser it works excactly as it should work.
As a WRT though (or phonegap app) it doesn't.
It doesn't give me the alert (used for debugging). It doesn't use the setTimeout. evaluates instantly or something. And the loading page is not shown.
yeah, sometimes it shows up once.
Another problem is that the loading div has a GIF img. It;s like a loading img.
But the thing is just static. It's like normal image instead of a animated GIF.
How is this possible.
Some notes to the code:
navigationObject.newPage(page);
This hides the current div i'm viewing and shows the div i pass to it.
window.adviceWizzardObject.init();
This makes an ajax request to a jsonrpc server and then evaluates the data json retreived and set's up the wizard.
Thanks in advance,
Erik
It does work,
But becouse of some caching or something the old versions were loaded or something like that.
Restarting my phone solved the problem.