Reputation: 10672
I have a string representing a stand-alone (and valid XHTML 1.0 Strict) HTML document, something like
var html = "<?xml ... <!DOCTYPE ... <html><head><style>...</style></head>
<body><table>...</table></body></html>";
The body of this HTML document contains a table whose CSS-style is described in the head of the HTML document.
I also have a DOM-tree of another HTML document. How can I include into this DOM-tree the DOM-tree of the table with the correct style (as described in the HTML-string)?
I am especially interested in a jQuery-based solution.
EDIT: To be more concrete, an example of an HTML-string that I'm talking about is embedded into this XML-document.
Upvotes: 7
Views: 4653
Reputation: 815
Here is my code that does what you described. Two things to note
var insertStr = "your string here";
var newDom = $(insertStr); // getting [meta style table.drs]
// I had to use the array as newDom.find would not work for me which I was expecting would work
var styleText = $(newDom[1]).text();
var tableElm = $(newDom[2]);
$(newDom[2]).appendTo('body');
var selectors = styleText.split(/{[\s\w\.\(\)':;"#%=/-]*}/);
var styles = styleText.match(/{[\s\w\.\(\)':;"#%=/-]*}/g);
for ( var i =0; i < styles.length; i++ ) {
var style = styles[i];
style = style.replace("{","");
style = style.replace("}","");
if ( selectors[i].indexOf("table.drs") > -1 ) { // identify top-level elm
// apply to self
tableElm.attr('style',style);
} else {
// apply to its children
tableElm.find(selectors[i]).attr('style',style);
}
}
Upvotes: 1
Reputation: 36502
I may be waaaay missing the point, but why not load the string into an IFRAME to render - this solves all the problems of having two separate DOM trees, and two separate sets of CSS rules, to me.
This code will do it:
$(function() {
var $frame = $('<iframe style="width:200px; height:100px; border: 0px;">');
$('body').html( $frame );
setTimeout( function() {
var doc = $frame[0].contentWindow.document;
var $body = $('body',doc);
$body.html(your_html);
}, 1 );
});
(which I lifted from here: putting html inside an iframe (using javascript))
Then if you are concerned about the size of the IFRAME, you can set it with:
$frame[0].style.height = $frame[0].contentWindow.document.body.offsetHeight + 'px';
$frame[0].style.width = $frame[0].contentWindow.document.body.offsetWidth + 'px';
Upvotes: 17
Reputation: 20572
Here's some test code demonstrating how I'd do this. The code extracts the <style>
element from the passed HTML and adds a custom ID to all the selectors, then injects the HTML into a <div>
with that ID. That way all the CSS passed by the HTML will only apply to the injected table:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Untitled Document</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var html = '<?xml version="1.0" encoding="UTF-8"?>\n\
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n\
<html xmlns="http://www.w3.org/1999/xhtml">\n\
<head>\n\
<title></title>\n\
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>\n\
<style type="text/css">\n\
div.dom { background-color: #ace; margin-bottom: 0.5em } table.drs { font-family: monospace; padding: 0.4em 0.4em 0.4em 0.4em; border: 1px solid black; margin-bottom: 2em; background-color: #eee; border-collapse: collapse }\n\
td { vertical-align: top; padding: 0.3em 0.3em 0.3em 0.3em; border: 1px solid black }\n\
td.op { vertical-align: middle; font-size: 110%; border: none }\n\
</style>\n\
</head>\n\
<body><table class="drs"><tr><td><div class="dom">[]</div></td></tr></table></body>\n\
</html>';
var style = html.match(/<style([^>]*)>([^<]*)<\/style>/);
var modifiedStyle = style[2].replace(/\s*(} |^|\n)\s*([^{]+)/g, " $1 #inserthere $2");
$(document).find('head').prepend("<style" + style[1] + ">" + modifiedStyle + "</style");
$("#inserthere").append($(html).children());
});
</script>
</head>
<body>
<div id="inserthere">
</div>
</body>
</html>
Obviously you will have to modify this for your situation, but it's a starting point.
Upvotes: 0
Reputation: 166122
Insert it into an inline frame.
Using a question I asked earlier, I have a solution like this:
First, have an iframe, with some id
<iframe id="preview" src="/empty.html"></iframe>
Then style it:
iframe#preview
{
margin: 30px 10px;
width: 90%;
height: 800px;
}
And here's a function to insert an html text into that frame (uses jquery)
function preview( html )
{
var doc = $("#preview")[0].contentWindow.document
var body = $('body',doc)
body.html( html );
}
I used this to successfully render the content of the html, including whatever embedded css it might include.
Upvotes: 1
Reputation: 17538
Instead of improving my other answer, I'd rather make a new one since I basically need to re-write it to work with how you want it and the old answer may help people for other reasons...
So, after extracting out the HTML-string from the XML you link to, you can go forth with it like this:
// First we'll extract out the parts of the HTML-string that we need.
var jq_html = $(html_from_xml);
var style = jq_html.find('head').find('style').html();
var style_string = style.toString();
var table_and_stuff = jq_html.find('body').html();
// If you want to place it into the existing page's DOM,
// we need to place it inside a new DIV with a known ID...
$('body').append('<div id="new_stuff"></div>');
$('#new_stuff').html(table_and_stuff);
// Now, we need to re-write the styles so that they only
// affect the content of the 'new_stuff' DIV:
styles_array = style_string.split('}');
var new_styles = '';
$.each(styles_array,function(i) {
if(i<(styles_array.length-1)) {
new_styles += "#new_stuff "+styles_array[i]+"}";
}
})
$('head').append('<style type="text/css">'+new_styles+'</style>');
And that should really do it. The trick is that CSS will choose the most specific style for the case. So, if you have a <td>
inside the "newstuff" div, it will get the style from the new stylesheet. If you have a <td>
outside of that "newstuff" div, it will get the old style.
I hope this solves your problem.
Upvotes: 1
Reputation: 17538
There's no sense in having two full DOM trees on the same page, so you'll want to extract out what you want and only use that.
Convert the string to a jQuery object and parse out what you need like this:
var html = "<html><head><style>...</style></head>
<body><table>...</table></body></html>";
// Not sure if you are a trying to merge to DOMs together or just
// trying to place what you want on the page so, I'm going to go
// with the former as it may be more tricky.
var other_html = "<html><head><style>...</style></head>
<body><!-- some stuff --></body></html>";
var jq_html = $(html);
var jq_other_html = $(other_html);
var style = jq_html.find('head').find('style');
var table_and_stuff = jq_html.find('body').html();
jq_other_html.find('head').append(style).end().append(table_and_stuff);
That should probably do it. The CSS should automatically be recognized by the browser once it's inserted into the page.
NOTE:
For the new CSS style sheet to only add new styles and not override your current one(s), you must prepend the sheet to the head tag and not append it. So, the last line would need to be like this instead:
jq_other_html.find('head').prepend(style).end().append(table_and_stuff);
Upvotes: 4