C# 中内联 CSS
我需要从 c# 中的样式表内联 css。
就像这是如何运作的。
http://www.mailchimp.com/labs/inlinecss.php
CSS很简单,只有类,没有花哨的选择器。
我正在考虑使用正则表达式 (?
有没有更简单的方法?或者已经用 c# 编写的东西?
更新 - 2010 年 9 月 16 日,
如果您的 html 也是有效的 xml,我已经能够想出一个简单的 CSS 内联器。它使用正则表达式来获取 元素中的所有样式。然后将 css 选择器转换为 xpath 表达式,并在任何预先存在的内联样式之前将内联样式添加到匹配元素。
请注意,CssToXpath 尚未完全实现,有些事情它还无法做到。
CssInliner.cs
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
namespace CssInliner
{
public class CssInliner
{
private static Regex _matchStyles = new Regex("\\s*(?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})",
RegexOptions.IgnoreCase
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
public List<Match> Styles { get; private set; }
public string InlinedXhtml { get; private set; }
private XElement XhtmlDocument { get; set; }
public CssInliner(string xhtml)
{
XhtmlDocument = ParseXhtml(xhtml);
Styles = GetStyleMatches();
foreach (var style in Styles)
{
if (!style.Success)
return;
var cssSelector = style.Groups["selector"].Value.Trim();
var xpathSelector = CssToXpath.Transform(cssSelector);
var cssStyle = style.Groups["style"].Value.Trim();
foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
{
var inlineStyle = element.Attribute("style");
var newInlineStyle = cssStyle + ";";
if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
{
newInlineStyle += inlineStyle.Value;
}
element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
}
}
XhtmlDocument.Descendants("style").Remove();
InlinedXhtml = XhtmlDocument.ToString();
}
private List<Match> GetStyleMatches()
{
var styles = new List<Match>();
var styleElements = XhtmlDocument.Descendants("style");
foreach (var styleElement in styleElements)
{
var matches = _matchStyles.Matches(styleElement.Value);
foreach (Match match in matches)
{
styles.Add(match);
}
}
return styles;
}
private static XElement ParseXhtml(string xhtml)
{
return XElement.Parse(xhtml);
}
}
}
CssToXpath.cs
using System.Text.RegularExpressions;
namespace CssInliner
{
public static class CssToXpath
{
public static string Transform(string css)
{
#region Translation Rules
// References: http://ejohn.org/blog/xpath-css-selectors/
// http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js
var regexReplaces = new[] {
// add @ for attribs
new RegexReplace {
Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
Replace = @"[@$1$2]"
},
// multiple queries
new RegexReplace {
Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
Replace = @"|"
},
// , + ~ >
new RegexReplace {
Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
Replace = @"$1"
},
//* ~ + >
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::*[1]/self::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/$2"
},
// all unescaped stuff escaped
new RegexReplace {
Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
Replace = @"[$1='$2']"
},
// all descendant or self to //
new RegexReplace {
Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"$1*$2$3"
},
new RegexReplace {
Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
Replace = @"$1//$2"
},
new RegexReplace {
Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
Replace = @"//"
},
// :first-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
Replace = @"*[1]/self::$1"
},
// :last-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
Replace = @"$1[not(following-sibling::*)]"
},
// :only-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
Replace = @"*[last()=1]/self::$1"
},
// :empty
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
Replace = @"$1[not(*) and not(normalize-space())]"
},
// |= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
},
// *= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(@$1,$2)]"
},
// ~= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
},
// ^= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[starts-with(@$1,$2)]"
},
// != attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[not(@$1) or @$1!=$2]"
},
// ids
new RegexReplace {
Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[@id='$1']"
},
// classes
new RegexReplace {
Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
},
// normalize multiple filters
new RegexReplace {
Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
Replace = @" and ($1)"
},
};
#endregion
foreach (var regexReplace in regexReplaces)
{
css = regexReplace.Regex.Replace(css, regexReplace.Replace);
}
return "//" + css;
}
}
struct RegexReplace
{
public Regex Regex;
public string Replace;
}
}
和一些测试
[TestMethod]
public void TestCssToXpathRules()
{
var translations = new Dictionary<string, string>
{
{ "*", "//*" },
{ "p", "//p" },
{ "p > *", "//p/*" },
{ "#foo", "//*[@id='foo']" },
{ "*[title]", "//*[@title]" },
{ ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" },
{ "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
};
foreach (var translation in translations)
{
var expected = translation.Value;
var result = CssInliner.CssToXpath.Transform(translation.Key);
Assert.AreEqual(expected, result);
}
}
[TestMethod]
public void HtmlWithMultiLineClassStyleReturnsInline()
{
#region var html = ...
var html = XElement.Parse(@"<html>
<head>
<title>Hello, World Page!</title>
<style>
.redClass {
background: red;
color: purple;
}
</style>
</head>
<body>
<div class=""redClass"">Hello, World!</div>
</body>
</html>").ToString();
#endregion
#region const string expected ...
var expected = XElement.Parse(@"<html>
<head>
<title>Hello, World Page!</title>
</head>
<body>
<div class=""redClass"" style=""background: red; color: purple;"">Hello, World!</div>
</body>
</html>").ToString();
#endregion
var result = new CssInliner.CssInliner(html);
Assert.AreEqual(expected, result.InlinedXhtml);
}
还有更多测试,但是,它们导入 html 文件作为输入和预期输出,我不会发布所有这些!
但我应该发布规范化扩展方法!
private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
return NormalizeSpaceRegex.Replace(data, @" ");
}
public static string NormalizeCharacter(this string data, char character)
{
var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
return normalizeCharacterRegex.Replace(data, character.ToString());
}
I need to inline css from a stylesheet in c#.
Like how this works.
http://www.mailchimp.com/labs/inlinecss.php
The css is simple, just classes, no fancy selectors.
I was contemplating using a regex (?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})+
to strip the rules from the css, and then attempting to do simple string replaces where the classes are called, but some of the html elements already have a style tag, so I'd have to account for that as well.
Is there a simpler approach? Or something already written in c#?
UPDATE - Sep 16, 2010
I've been able to come up with a simple CSS inliner provided your html is also valid xml. It uses a regex to get all the styles in your <style />
element. Then converts the css selectors to xpath expressions, and adds the style inline to the matching elements, before any pre-existing inline style.
Note, that the CssToXpath is not fully implemented, there are some things it just can't do... yet.
CssInliner.cs
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Xml.XPath;
namespace CssInliner
{
public class CssInliner
{
private static Regex _matchStyles = new Regex("\\s*(?<rule>(?<selector>[^{}]+){(?<style>[^{}]+)})",
RegexOptions.IgnoreCase
| RegexOptions.CultureInvariant
| RegexOptions.IgnorePatternWhitespace
| RegexOptions.Compiled
);
public List<Match> Styles { get; private set; }
public string InlinedXhtml { get; private set; }
private XElement XhtmlDocument { get; set; }
public CssInliner(string xhtml)
{
XhtmlDocument = ParseXhtml(xhtml);
Styles = GetStyleMatches();
foreach (var style in Styles)
{
if (!style.Success)
return;
var cssSelector = style.Groups["selector"].Value.Trim();
var xpathSelector = CssToXpath.Transform(cssSelector);
var cssStyle = style.Groups["style"].Value.Trim();
foreach (var element in XhtmlDocument.XPathSelectElements(xpathSelector))
{
var inlineStyle = element.Attribute("style");
var newInlineStyle = cssStyle + ";";
if (inlineStyle != null && !string.IsNullOrEmpty(inlineStyle.Value))
{
newInlineStyle += inlineStyle.Value;
}
element.SetAttributeValue("style", newInlineStyle.Trim().NormalizeCharacter(';').NormalizeSpace());
}
}
XhtmlDocument.Descendants("style").Remove();
InlinedXhtml = XhtmlDocument.ToString();
}
private List<Match> GetStyleMatches()
{
var styles = new List<Match>();
var styleElements = XhtmlDocument.Descendants("style");
foreach (var styleElement in styleElements)
{
var matches = _matchStyles.Matches(styleElement.Value);
foreach (Match match in matches)
{
styles.Add(match);
}
}
return styles;
}
private static XElement ParseXhtml(string xhtml)
{
return XElement.Parse(xhtml);
}
}
}
CssToXpath.cs
using System.Text.RegularExpressions;
namespace CssInliner
{
public static class CssToXpath
{
public static string Transform(string css)
{
#region Translation Rules
// References: http://ejohn.org/blog/xpath-css-selectors/
// http://code.google.com/p/css2xpath/source/browse/trunk/src/css2xpath.js
var regexReplaces = new[] {
// add @ for attribs
new RegexReplace {
Regex = new Regex(@"\[([^\]~\$\*\^\|\!]+)(=[^\]]+)?\]", RegexOptions.Multiline),
Replace = @"[@$1$2]"
},
// multiple queries
new RegexReplace {
Regex = new Regex(@"\s*,\s*", RegexOptions.Multiline),
Replace = @"|"
},
// , + ~ >
new RegexReplace {
Regex = new Regex(@"\s*(\+|~|>)\s*", RegexOptions.Multiline),
Replace = @"$1"
},
//* ~ + >
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])~([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])\+([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/following-sibling::*[1]/self::$2"
},
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*])>([a-zA-Z0-9_\-\*])", RegexOptions.Multiline),
Replace = @"$1/$2"
},
// all unescaped stuff escaped
new RegexReplace {
Regex = new Regex(@"\[([^=]+)=([^'|""][^\]]*)\]", RegexOptions.Multiline),
Replace = @"[$1='$2']"
},
// all descendant or self to //
new RegexReplace {
Regex = new Regex(@"(^|[^a-zA-Z0-9_\-\*])(#|\.)([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"$1*$2$3"
},
new RegexReplace {
Regex = new Regex(@"([\>\+\|\~\,\s])([a-zA-Z\*]+)", RegexOptions.Multiline),
Replace = @"$1//$2"
},
new RegexReplace {
Regex = new Regex(@"\s+\/\/", RegexOptions.Multiline),
Replace = @"//"
},
// :first-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):first-child", RegexOptions.Multiline),
Replace = @"*[1]/self::$1"
},
// :last-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):last-child", RegexOptions.Multiline),
Replace = @"$1[not(following-sibling::*)]"
},
// :only-child
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):only-child", RegexOptions.Multiline),
Replace = @"*[last()=1]/self::$1"
},
// :empty
new RegexReplace {
Regex = new Regex(@"([a-zA-Z0-9_\-\*]+):empty", RegexOptions.Multiline),
Replace = @"$1[not(*) and not(normalize-space())]"
},
// |= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\|=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[@$1=$2 or starts-with(@$1,concat($2,'-'))]"
},
// *= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\*=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(@$1,$2)]"
},
// ~= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)~=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@$1),' '),concat(' ',$2,' '))]"
},
// ^= attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\^=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[starts-with(@$1,$2)]"
},
// != attrib
new RegexReplace {
Regex = new Regex(@"\[([a-zA-Z0-9_\-]+)\!=([^\]]+)\]", RegexOptions.Multiline),
Replace = @"[not(@$1) or @$1!=$2]"
},
// ids
new RegexReplace {
Regex = new Regex(@"#([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[@id='$1']"
},
// classes
new RegexReplace {
Regex = new Regex(@"\.([a-zA-Z0-9_\-]+)", RegexOptions.Multiline),
Replace = @"[contains(concat(' ',normalize-space(@class),' '),' $1 ')]"
},
// normalize multiple filters
new RegexReplace {
Regex = new Regex(@"\]\[([^\]]+)", RegexOptions.Multiline),
Replace = @" and ($1)"
},
};
#endregion
foreach (var regexReplace in regexReplaces)
{
css = regexReplace.Regex.Replace(css, regexReplace.Replace);
}
return "//" + css;
}
}
struct RegexReplace
{
public Regex Regex;
public string Replace;
}
}
And some tests
[TestMethod]
public void TestCssToXpathRules()
{
var translations = new Dictionary<string, string>
{
{ "*", "//*" },
{ "p", "//p" },
{ "p > *", "//p/*" },
{ "#foo", "//*[@id='foo']" },
{ "*[title]", "//*[@title]" },
{ ".bar", "//*[contains(concat(' ',normalize-space(@class),' '),' bar ')]" },
{ "div#test .note span:first-child", "//div[@id='test']//*[contains(concat(' ',normalize-space(@class),' '),' note ')]//*[1]/self::span" }
};
foreach (var translation in translations)
{
var expected = translation.Value;
var result = CssInliner.CssToXpath.Transform(translation.Key);
Assert.AreEqual(expected, result);
}
}
[TestMethod]
public void HtmlWithMultiLineClassStyleReturnsInline()
{
#region var html = ...
var html = XElement.Parse(@"<html>
<head>
<title>Hello, World Page!</title>
<style>
.redClass {
background: red;
color: purple;
}
</style>
</head>
<body>
<div class=""redClass"">Hello, World!</div>
</body>
</html>").ToString();
#endregion
#region const string expected ...
var expected = XElement.Parse(@"<html>
<head>
<title>Hello, World Page!</title>
</head>
<body>
<div class=""redClass"" style=""background: red; color: purple;"">Hello, World!</div>
</body>
</html>").ToString();
#endregion
var result = new CssInliner.CssInliner(html);
Assert.AreEqual(expected, result.InlinedXhtml);
}
There are more tests, but, they import html files for the input and expected output and I'm not posting all that!
But I should post the Normalize extension methods!
private static readonly Regex NormalizeSpaceRegex = new Regex(@"\s{2,}", RegexOptions.None);
public static string NormalizeSpace(this string data)
{
return NormalizeSpaceRegex.Replace(data, @" ");
}
public static string NormalizeCharacter(this string data, char character)
{
var normalizeCharacterRegex = new Regex(character + "{2,}", RegexOptions.None);
return normalizeCharacterRegex.Replace(data, character.ToString());
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
我在 Github 上有一个使 CSS 内联的项目。它非常简单,并且支持移动样式。在我的博客上阅读更多内容:http://martinnormark.com/move-css-inline-premailer -net
I have a project on Github that makes CSS inline. It's very simple, and support mobile styles. Read more on my blog: http://martinnormark.com/move-css-inline-premailer-net
既然您当前的实现已经完成了 90%,为什么不使用现有框架而是用 HTML 解析器替换 XML 解析呢?其中比较流行的包之一是 HTML Agility Pack。它支持 XPath 查询,甚至具有类似于为 XML 提供的标准 .NET 接口的 LINQ 接口,因此它应该是一个相当简单的替代品。
Since you're already 90% of the way there with your current implementation, why don't you use your existing framework but replace the XML parsing with an HTML parser instead? One of the more popular ones out there is the HTML Agility Pack. It supports XPath queries and even has a LINQ interface similar to the standard .NET interface provided for XML so it should be a fairly straightforward replacement.
由于这个选项在其他答复中不是很清楚,我认为它值得一个简单的答案。
使用PreMailer.Net。
您所要做的就是:
输入:
您就完成了!
顺便说一句,您可能需要添加
using
指令来缩短该行。当然,上面的链接中有更多使用信息。
As this option is not very clear in the other replies, I think it deserves a straightforward answer.
Use PreMailer.Net.
All you have to do is:
Type this:
And you are done!
BTW, you may want to add a
using
directive to shorten that line.More usage info in the link above, of course.
很好的问题。
我不知道是否有 .NET 解决方案,但我发现了一个名为 Premailer 的 Ruby 程序 声称内联 CSS。如果您想使用它,您有几个选择:
Excellent question.
I have no idea if there is a .NET solution, but I found a Ruby program called Premailer that claims to inline CSS. If you want to use it you have a couple options:
我建议使用实际的 CSS 解析器而不是正则表达式。您不需要解析完整的语言,因为您主要对复制感兴趣,但无论如何,此类解析器都是可用的(对于 .NET 也是如此)。例如,看一下antlr的语法列表,特别是CSS 2.1 语法 或 CSS3 语法。如果您不介意次优结果(其中内联样式可能包含重复定义),您可以删除这两种语法的大部分内容,但要做到这一点,您需要了解内部 CSS 逻辑一些能够解析速记属性。
然而,从长远来看,这肯定比一系列永无休止的临时正则表达式修复要少很多工作。
I'd recommend using an actual CSS parser rather than Regexes. You don't need to parse the full language since you're interested mostly in reproduction, but in any case such parsers are available (and for .NET too). For example, take a look at antlr's list of grammars, specifically a CSS 2.1 grammar or a CSS3 grammar. You can possibly strip large parts of both grammars if you don't mind sub-optimal results wherein inline styles may include duplicate definitions, but to do this well, you'll need some idea of internal CSS logic to be able to resolve shorthand attributes.
In the long run, however, this will certainly be a lot less work than a neverending series of adhoc regex fixes.
这是一个想法,为什么不拨打 http://www.mailchimp.com /labs/inlinecss.php 使用 C#。从使用 firebug 的分析来看,post 调用需要 2 个参数 html 和 strip ,它们接受值(开/关),结果位于名为 text 的参数中。
这是关于如何使用 c# 进行 post 调用的示例
Here is an idea, why dont you make a post call to http://www.mailchimp.com/labs/inlinecss.php using c#. from analysis using firebug it looks like the post call needs 2 params html and strip which takes values (on/off) the result is in a param called text.
here is a sample on how to make a post call using c#
Chad,你一定要添加 CSS 内联吗?或者您可以通过在
中添加
块来获得更好的结果吗?这本质上将取代对 CSS 文件的引用的需要,并维护实际内联规则覆盖标头/引用的 css 文件中设置的规则。
(抱歉,忘记添加代码的引号)
Chad, do you necessarily have to add the CSS inline? Or could you maybe be better off by adding a
<style>
block to your<head>
? This will in essence replace the need for a reference to a CSS file as well plus maintain the rule that the actual inline rules override the ones set in the header/referenced css file.(sorry, forgot to add the quotes for code)
我会推荐这样的字典:
我会解析 css 来填充这个 cssDictionary。
(添加“style-type”、“style-property”、“value”。例如:
之后我最好将 HTML 转换为 XmlDocument。
您可以通过其子级递归地运行文档节点,并查找其父级(这甚至可以让您能够使用选择器)。
您可以在每个元素上检查元素类型、id 和类,然后浏览 cssDictionary 以将该元素的任何样式添加到 style 属性中(当然,您可以这样做)。如果它们具有重叠的属性,则可能需要按出现的顺序放置它们(并在最后添加现有的内联样式)。
完成后,您将 xmlDocument 作为字符串发出并删除第一行(
) 这应该会为您留下一个带有内联 css 的有效 html 文档。
当然,它可能看起来有点像黑客,但最终我认为这是一个非常可靠的解决方案,可以确保稳定性并且完全满足您的需求。
I would recommend a dictonary like this:
I would parse the css to fill this cssDictionary.
(Adding 'style-type', 'style-property', 'value'. In example:
After that I would preferably convert the HTML to an XmlDocument.
You can recursively run through the documents nodes by it's children and also look up it's parents (This would even enable you being able to use selectors).
On each element you check for the element type, the id and the class. You then browse through the cssDictionary to add any styles for this element to the style attribute (Granted, you might want to place them in order of occurrence if they have overlapping properties (And add the existing inline styles the last).
When you're done, you emit the xmlDocument as a string and remove the first line (
<?xml version="1.0"?>
) This should leave you with a valid html document with inline css.Sure, it might half look like a hack, but in the end I think it's a pretty solid solution that ensures stability and quite does what you seem to be looking for.