I don't know how to remove the html wrapper in the iframe.
An example:
var content = '<html><body>A page</body></html>';
$('iframe').contents().find('html').html(content);
Even if it does not look like it in the developer tools, the above now have 2 html tags.
var html = $('iframe').contents().find('html html').height();
When using the above I get a height. When only using one html in the selector I just get 0. It proves that there are 2 html tags.
How can I get rid of the html wrapper in the iframe?
In my case I will not modify the content variable. It's fetched from elsewhere.
UPDATE
I tried this:
$('iframe').find('html').replaceWith(content);
But it gave me:
Failed to execute 'replaceChild' on 'Node': Nodes of type '#text' may not be inserted inside nodes of type '#document'.
From my experience, you should use document.write:
var content = '<html><body>A page</body></html>';
var iframeDoc = $('iframe')[0].contentDocument; // jQuery is just for the example of course
iframeDoc.write(content);
See -DEMO-
If you want to overwrite previous set content used with snippet before, you will need to call iframeDoc.open(); before calling iframeDoc.write(content);. It doesn't really makes sense but it is...
Related
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
I'm trying to use Javascript to retrieve elements from an HTML widget on the page. I can't just use the usual document.getelementsbyclassname etc. as the widget is treated as an embedded section of the page.
I have tried this:
var iframe = document.getElementsByClassName("widgetclassname")[0];
var iframeDocument = iframe.contentDocument || iframe.contentWindow.document;
I've used that in the past for iframes but not for this. I'm getting this error:
Uncaught TypeError: Cannot read property 'contentDocument' of undefined
I've tried adding a delay and moving around items in case it's a timing thing (trying to get things before it's loaded properly) but it made no difference.
I can't use ID instead of classname as the widget doesn't have one and I can't change that.
Cannot read property 'contentDocument' of undefined means "Hey! iframe is undefined!" Which means that your getElementsByClassName() is returning an empty node list.
We cannot help you further without a way of looking at the document you are trying to query. Are you using the developer tools to ensure that the <iframe> you want has that class on it? Exactly, no typos, no case changes?
Alternatively, you can try using:
// first iframe in the document
var iframe = document.querySelector('iframe');
// third iframe in the document
var iframe = document.querySelectorAll('iframe')[2];
Edit: in response to our chat discussion, the problem is that the page is dynamically loading content, and the script you are trying to run is taking place before that content has been loaded.
A simple hack is to wait for the element you want to load and then run the code you want. For example:
document.title = "Search Results - AvenaGo";
var waitForHotel = setInterval(function(){
var hotelName = document.querySelector('.hotel_page-hotel_name');
if (hotelName) {
clearInterval(waitForHotel);
document.title = hotelName.textContent + ' - AvenaGo';
}
}, 100);
Every 100ms this code will run and check to see if an element with the class hotel_page-hotel_name has loaded yet. If it has, it will stop checking, and modify the title of the page based on the text in that element.
After an AJAX request I get a string with some HTML data like this:
var test = '<div id="test-options"><option value="">Select</option><option value="true#153237119">XS</option><option value="true#153237120">S</option><option value="true#153237121">M</option><option value="true#153237122">L</option><option value="true#153237123">XL</option><option value="true#153237124">XXL</option></div><div class="athoerstuff">Ather stuff here</div>';
I want to append this and after it's appended get only the content of div without the wrapper:
var li = $(test).appendTo('form');
$("body").find('#test-options').html();
-------------------------------
The problem was coming only on IE9 because the options without having the wrapper.
IE9 was looking at the options inside the div and realized that this is invalid. That is why, IE9 was not getting me the options.
I added a select wrapper in the server side script where I was making the request (option values) and, instead of:
$('#test-options').html();
I added:
$('#test-options select').html();
The result result was same as previous code, but now working on IE9 as well.
Thank you all for your help.
Maybe somebody can tell me why IE9 is not getting the options without the select wrapper and other browsers do?
If you are trying to get the options html then you can simply do like below,
$(test).unwrap().html();
instead of appending to body and retrieving the content.
http://jsfiddle.net/XYhbg/
The above is under the assumption with the data that you had in the test variable. Let me know if it is any different.
I'm working with classic ASP.
I have an 2 includes that have 2 different forms on them. They are both unique in name. However, when I try to read the value of the one the elements in the 2nd form I get an error saying it is null. However, when I view the source in Firebug I can see that in face there is a value in that element.
My javascript code:
console.log(document.getElementById('focusValue').value);
Output from firebug:
<input id="focusValue" type="hidden" value="1006" name="focusValue">
Is there something I need to do because there are 2 forms on this "rendered" screen? The only other thing I think I should mention is that these pages are in an iFrame. Not sure if that really matters...
An iFrame creates a separate document within the containing document, you need to get a reference to that document before you can access its content. There is a reasonable tutorial at http://www.dyn-web.com/tutorials/iframes/.
If you only have one iFrame in the page, then you can reference it by:
var frame = window.frames[0];
Or you could use an id with getElementById. To get a reference to the document:
var doc;
if (frame) {
doc = frame.contentDocument || frame.contentWindow.document;
}
Now you can get to the input element:
var input = doc && doc.getElementById('focusValue');
Of course this all depends on you complying with the same origin policy, otherwise you can't access the frame content.
Can't see your page, so it's hard to debug. Assuming the script runs AFTER the forms. The only thing i can think is that there is more than one element on the page with the id "focusValue".
What happens when you console.log(document.getElementById('focusValue'))
I have written some code that takes a string of html and cleans away any ugly HTML from it using jQuery (see an early prototype in this SO question). It works pretty well, but I stumbled on an issue:
When using .append() to wrap the html in a div, all script elements in the code are evaluated and run (see this SO answer for an explanation why this happens). I don't want this, I really just want them to be removed, but I can handle that later myself as long as they are not run.
I am using this code:
var wrapper = $('<div/>').append($(html));
I tried to do it this way instead:
var wrapper = $('<div>' + html + '</div>');
But that just brings forth the "Access denied" error in IE that the append() function fixes (see the answer I referenced above).
I think I might be able to rewrite my code to not require a wrapper around the html, but I am not sure, and I'd like to know if it is possible to append html without running scripts in it, anyway.
My questions:
How do I wrap a piece of unknown html
without running scripts inside it,
preferably removing them altogether?
Should I throw jQuery out the window
and do this with plain JavaScript and
DOM manipulation instead? Would that help?
What I am not trying to do:
I am not trying to put some kind of security layer on the client side. I am very much aware that it would be pointless.
Update: James' suggestion
James suggested that I should filter out the script elements, but look at these two examples (the original first and the James' suggestion):
jQuery("<p/>").append("<br/>hello<script type='text/javascript'>console.log('gnu!'); </script>there")
keeps the text nodes but writes gnu!
jQuery("<p/>").append(jQuery("<br/>hello<script type='text/javascript'>console.log('gnu!'); </script>there").not('script'))`
Doesn't write gnu!, but also loses the text nodes.
Update 2:
James has updated his answer and I have accepted it. See my latest comment to his answer, though.
How about removing the scripts first?
var wrapper = $('<div/>').append($(html).not('script'));
Create the div container
Use plain JS to put html into div
Remove all script elements in the div
Assuming script elements in the html are not nested in other elements:
var wrapper = document.createElement('div');
wrapper.innerHTML = html;
$(wrapper).children().remove('script');
var wrapper = document.createElement('div');
wrapper.innerHTML = html;
$(wrapper).find('script').remove();
This works for the case where html is just text and where html has text outside any elements.
You should remove the script elements:
var wrapper = $('<div/>').append($(html).remove("script"));
Second attempt:
node-validator can be used in the browser:
https://github.com/chriso/node-validator
var str = sanitize(large_input_str).xss();
Alternatively, PHPJS has a strip_tags function (regex/evil based):
http://phpjs.org/functions/strip_tags:535
The scripts in the html kept executing for me with all the simple methods mentioned here, then I remembered jquery has a tool for this (since 1.8), jQuery.parseHTML. There's still a catch, according to the documentation events inside attributes(i.e. <img onerror>) will still run.
This is what I'm using:
var $dom = $($.parseHTML(d));
$dom will be a jquery object with the elements found