remove elements by javascript without content shift [duplicate] - javascript

This question already has answers here:
How to remove elements in the content script before showing the page?
(1 answer)
Deleting DOM elements before the Page is displayed to the screen (in a Chrome extension)
(1 answer)
Closed last month.
I created a chrome extension which is used to block advertisements elements from some websites.
My content script is as below:
var selectors = [
...
];
for(var i in selectors){
if(selectors.hasOwnProperty(i)){
var elements = document.querySelectorAll(selectors[i]);
console.log('elements: ', elements);
for (var k in elements) {
if (elements.hasOwnProperty(k)) {
elements[k].parentElement.removeChild(elements[k]);
}
}
}
}
when I wrap the code in document.addEventListener("DOMContentLoaded", function() {}, it shift the content and when I don't wrap the code in DOMContentLoaded the elements list is empty.
what should I do to remove the advertisement element without content shift?

Rather than waiting for the whole document to load, you could consider inserting a <style> tag immediately, and have rules inside that hide those selectors.
const selectors = [ ... ];
// run ASAP:
document.body.appendChild(document.createElement('style'))
.textContent = selectors.join(', ') + ' { display: none }';
// Run after document loads:
document.addEventListener("DOMContentLoaded", () => {
// ...
});
This will result in a <style> tag with content like
.ad1, .ad2, .ad3 { display: none }
This way, the removal of the bad elements won't result in content shifting, for the most part, because they already have display: none.
Another benefit of such a stylesheet is that it'll take effect immediately, without any additional JavaScript, whenever an element is inserted. So, for example, if a page inserts an ad somewhere every 30 seconds, the CSS rules in the <style> can prevent it from being seen without having to have additional JS to deal with the dynamically generated content.
Arguably, if you just hide the ads with the stylesheet, that may be all you need to accomplish. You can additionally remove them from the DOM entirely if you want (perhaps lowering bandwidth usage), but the user probably won't notice a difference between hidden ads and removed ads.
A MutationObserver attached on pageload is another option - instead of waiting for everything to finish first, you can remove blacklisted elements as soon as they get inserted into the DOM.

Related

Remove all elements from website except X

I'm not really familiar with Javascript, and even less with how Javascript works in Chrome's F12 developer tools. What I'm trying to do is have a favorite which, when clicked on, loads a web page but removes some of the clutter of the page which is loaded (I don't really care if it removes it before the page is loaded, or loads it and then removes it)
For now, I'm trying to figure out how to remove all elements except the one I want to keep (and its' children), namely, one which has the following html:
<div>
<ul class="c-list-news u-relative" data-load-more-content>...</ul>
</div>
I'm trying the following (from what I could find on SO), but I can't find the right selector (or I'm doing something else wrong, not quite sure):
var elem = document.querySelectorAll('body *:not(div ul.c-list-news, div ul.c-list-news *)');
for(var i=0;i<elem.length;i++) {
elem[i].parentElement.removeChild(elem[i]);
}
(PS : I haven't yet looked into how to put it into a favorite/extension, it will come later)
It's probably easier than you realize. :-) You can get the first element matching .c-list-news like this:
const cListNews = document.querySelector(".c-list-news");
If you want to keep its parent, just add .parentNode to that:
const divContainer = document.querySelector(".c-list-news").parentNode;
Then, wipe out body entirely:
document.body.innerHTML = "";
...and put the element back:
document.body.appendChild(cListNews); // Or `divContainer`
I'm not sure I'd expect the page to continue to be readable, though, since of course this completely changes where the element is in the DOM, which may well make the CSS fail.
You can't make a bookmark (favorite) that both loads the page and does this in one go, because javascript: bookmarks work within the context of the current page. You could use something like TamperMonkey which is an extension that lets you run a script automatically when you go to matching URLs.
But you can make a bookmark that you use when you're already on the page: Just use the javascript: pseudo-protocol and follow it with JavaScript code. For instance:
javascript:var divContainer %3D document.querySelector(".c-list-news").parentNode%3Bdocument.body.innerHTML %3D ""%3Bdocument.body.appendChild(divContainer)%3Bconsole.log("done")%3B
I created that by simply removing line breaks from the code (optional), running the code through encodeURIComponent, and putting javascript: on the front. (Some folks would also convert spaces to %20.)
Save the element to keep to a variable. Remove all nodes from the body, or the element that you want, and add the element to keep. Example:
let elementToKeep = document.getElementById('side');
const myNode = document.getElementsByTagName("body")[0];
while (myNode.firstChild) {
myNode.removeChild(myNode.firstChild);
}
myNode.appendChild(elementToKeep);
Using the removeChild method is faster that setting the innerHtml as empty string.
Check here: Remove all child elements of a DOM node in JavaScript

Remove dynamic script tag on src match

I have a web page which is working fine on my local machine, but when it is moved to the server, an ad script from google overwrites page image src.
I want to find that particular script tag with that src and remove it. I know the url, but can not add an id or modify the DOM. Please help.
for eg:
<script src="one.js"><script>
<script src="two.js"><script>
<script src="three.js"><script>
so if
var scripts = document.getElementsByTagName ( "script" );
var l = scripts.length;
for ( var i = 0; i < l; ++ i ) {
if ( scripts[i].src ="one.js") {
//remove that particular script tag with one.js
}
}
As Paul stated in the comments, removing an already loaded script wont do any change. But, for the sake of it:
$('script[src*="one.js"]').remove();
with jQuery (as this question is tagged).
Edit: for more info about attribute selectors (like *="string") have read here
Not quite an answer per say, but still i have a gut feeling that i should post this workaround.
So if I were you, maybe I'd try to trap the script by creating the fake set of elements that the script is meddling with and then verify if the respective data has changed ad then delete that div.
Better explained step by step.
Introduce a fake div with similar Dom before our concerned div. Now if there are two divs with same Id then that script shall target the first div with that id.
You check if the content of our Trojan div is changed, if yes then remove it from the Dom.
Now execute your script.
Also as a counter measure just remove all the events that are delegated to that div. If the rogue script is jquery based, even better, since you can remove the respective event and bind your own, using the .off() function. As shown below..
$("#xyz").off().on('click', function(){});

Javascript executes in wrong order

I have to use a website (ticket system) with a select element, that contains currently 9994 options. This is not changeable and has to be accepted as is.
When I work on a ticket on that website, I have to select a specific entry from that select. I have a total set of around 30 entries I have to choose from. I don't care for the other entries.
The required 30 entries can be seperated into 3 patterns for a RegEx filter.
So I decided to use Greasemonkey+JQuery to clean that select element up, so I can easily and quickly find the entries I am looking for.
The filtering is working fine, but it takes time (of course it does...), so I want to show a little "please wait" div as overlay, while the filter is running to give some kind of user feedback.
On page load I create the overlay:
$("body").append('<div id="pleaseWaitOverlay" style="position: fixed; top: 0; left: 0; bottom: 0; right: 0; background-color: rgb(255,255,255);">HALLO WELT</div>');
$("#pleaseWaitOverlay").hide();
//This is the select element with the "few" entries
fixedInReleaseElement = $('select[name=resolvedReleasePath]');
//Adding buttons to filter for one of the patterns are also added on page load
If I press on one of the filter buttons, the following function will be called:
function filterFixedInReleaseList(filterFor) {
$("#pleaseWaitOverlay").show();
//$("#pleaseWaitOverlay").show().delay(500).show(); - or as hack without success...
var pattern;
//Based on "filterFor" parameter, the required pattern will be used.
// [MORE CODE]
fixedInReleaseElement.find("option").each(function() {
var currentOption = $(this);
if (pattern === "") {
currentOption.show();
}
else {
if (pattern.test(currentOption.text())) {
currentOption.show();
}
else {
currentOption.hide();
}
}
});
//$("#pleaseWaitOverlay").hide();
}
But somehow, the filter will take place and THEN the overlay will be shown.
Please note:
Currently, the .hide() lines are commented out, as the popup would not be shown (or rather seen) at all with those lines executed.
The .show().delay(500).show() was a try to kind of hack it, but it changed absolutly nothing.
I also tried fixedInReleaseElement.find("option").delay(1000).each() without success. I appears that delay does not work at all?
So, what is the problem here? Why is the overlay shown after the filter has been executed?
The complete Greasemonkey script can be found here:
http://pastebin.com/auafMSR1
A browser tab only has one thread, that is shared between JavaScript and UI updates. Thus, if your JavaScript is running, the UI is not getting updated.
So, this:
function doSomethingLongWithOverlayWrongly() {
$x.show();
doSomethingLong();
$x.hide();
}
will set appropriate attributes of $x to be hidden, then do something long, then set the attributes back; and when doSomethingLongWithOverlayWrongly (and all the computation that is in its future) finally exits and relinquishes control of the executing thread, the browser will take note that some attributes were changed, and repaint if necessary (but it's not, since the element was set to invisible, and is now still set to invisible).
Do this instead:
function doSomethingLongWithOverlayCorrectly() {
$x.show();
setTimeout(function() {
doSomethingLong();
$x.hide();
}, 0);
}
This will set $x to be hidden, then schedule a timeout, then exit. The browser takes a look, sees a repaint is in order, and shows your overlay. Then the timeouted function gets run, does something long, and sets $x to be hidden again. When it exits, the browser takes another look, sees that a repaint is required, and hides your overlay.

Long running jQuery method - how to refresh dom? [duplicate]

This question already has answers here:
refresh DOM after append element
(5 answers)
Closed 8 years ago.
UPDATE
I think I have to be a little more precise.
I have this long running code that adds content to my DIVs.
This is an example of the code:
m = jQuery('#test').clone();
//do some work
jQuery('#test2').append(m);
The Problem is that this can really take a while because sometimes I am adding 100 items. And the content only appears on the page after all 100 items have been added. So the user had to wait like 30 seconds or so.
What I would like to is to update the #test2 - DIV after adding 10 elements or so and then continue adding elements
Is there a way to refresh the DOM or (better) refresh the test2 - DIV and then continue adding Elements ?
OLD Description
I have a long running jQuery method that clones DIVs and appends them to the page.
It looks like this:
m = jQuery('#test').clone();
//do some work
jQuery('#test2').append(m);
This code is called several times (sometimes up to 100 times) and it takes pretty long for the content to appear in the page.
Is there a way to refresh the dom and print the content onto the page so that the user is not getting bored because nothing happens ?
You can do what you want using setTimeout. It's like the same logic we use for doing animations.
// Code goes here
function load(index) {
var m = jQuery('#test').clone();
m.html("Element" + index)
jQuery('#test2').append(m);
if(index < 100) {
setTimeout(function() {
load(index + 1);
},100);
}
}
$(document).ready(function(){
load(0);
});
here is a plunker
This is not exactly what you want, but why don't you put a spinner on your page whilst this is loading, which then goes away when the operations are done? It may be a cleaner method of doing things rather than the page just randomly refreshing for the user whilst they are using it.

Is it possible to reliably insert a HTML element at script's location?

I'm writing a Javascript file which will be a component in a webpage. I'd like it to be simple to use - just reference the script file in your page, and it is there. To that end however there is a complication - where should the HTML go that the Javascript generates? One approach would be to require a placeholder element in the page with a fixed ID or class or something. But that's an extra requirement. It would be better if the HTML was generated at the location that the script is placed (or, at the start of body, if the script is placed in head). Also, for extra customizability, if the fixed ID was found, the HTML would be placed inside that placeholder.
So I'm wondering - how do I detect my script's location in the page? And how do I place HTML there? document.write() comes to mind, but that is documented as being pretty unreliable. Also it doesn't help if the script is in the head. Not to mention what happens if my script is loaded dynamically via some AJAX call, but I suppose that can be left as an unsupported scenario.
I am doing that with this code...
// This is for Firefox only at the moment.
var thisScriptElement = document.currentScript,
// Generic `a` element for exploiting its ability to return `pathname`.
a = document.createElement('a');
if ( ! thisScriptElement) {
// Iterate backwards, to look for our script.
var scriptElements = document.body.getElementsByTagName('script'),
i = scriptElements.length;
while (i--) {
if ( ! scriptElements[i].src) {
continue;
}
a.href = scriptElements[i].src;
if (a.pathname.replace(/^.*\//, '') == 'name-of-your-js-code.js') {
thisScriptElement = scriptElements[i];
break;
}
}
}
Then, to add your element, it's simple as...
currentScript.parentNode.insertBefore(newElement, currentScript);
I simply add a script element anywhere (and multiple times if necessary) in the body element to include it...
<script type="text/javascript" src="somewhere/name-of-your-js-code.js?"></script>
Ensure the code runs as is, not in DOM ready or window's load event.
Basically, we first check for document.currentScript, which is Firefox only but still useful (if it becomes standardised and/or other browsers implement it, it should be most reliable and fastest).
Then I create a generic a element to exploit some of its functionality, such as extracting the path portion of the href.
I then iterate backwards over the script elements (because in parse order the last script element should be the currently executing script), comparing the filename to what we know ours is called. You may be able to skip this, but I am doing this to be safe.
document.write is very reliable if used as you indicate (a default SharePoint 2010 page uses it 6 times). If placed in the head, it will write content to immediately after the body element. The trick is to build a single string of HTML and write it in one go, don't write snippets of half-formed HTML.
An alternative is to use document.getElementsByTagName('script') while the document is loading and assume the the last one is the current script element. Then you can look at the parent and if it's the head, use the load or DOM ready event to add your elements after the body. Otherwise, just add it before or after the script element as appropriate.

Categories

Resources