且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

如何使用Google Apps脚本将CSS页面更改为使用内联样式的页面?

更新时间:2023-12-02 11:35:46

There are numerous online tools that do this conversion, so you could leverage one of them from Google Apps Script. (If you only need to do this once in a while, why not just use one of those services?)

Here's an example script, that builds on the getElementByVal() function from Does Google Apps Script have something like getElementById?.

inline() function

/**
 * Convert html containing <style> tags to instead have inline css.
 *
 * This example uses an online service from MailChimp Labs, but
 * the same principle could be used to leverage several other
 * online providers.
 *
 * @param  {Text}  htmlWstyle  A block of HTML text including
 *                             <style>..</style> tags.
 *
 * @returns {Text}             Same HTML, converted to inline css.
 */
function inline(htmlWstyle) {
  // Generate a POST request to inline css web tool.
  var payload =
  {
    "html" : htmlWstyle,
    "strip" : "checked"
  };

  // Because payload is a JavaScript object, it will be interpreted as
  // an HTML form. (We do not need to specify contentType; it will
  // automatically default to either 'application/x-www-form-urlencoded'
  // or 'multipart/form-data')

  var options =
  {
    "method" : "post",
    "payload" : payload,
    "muteHttpExceptions" : true
  };

  var url = "http://beaker.mailchimp.com/inline-css";
  var response = UrlFetchApp.fetch(url, options);

  // The html from this service is non-compliant, so we need
  // to massage it to satisfy the XmlService.
  var badlink = new RegExp('<link (.*?)[\/]*>',"igm");
  var badmeta = new RegExp('<meta (.*?)[\/]*>',"igm");
  var badinput = new RegExp('<input (.*?)[\/]*>',"igm");
  var xml = response.getContentText()
                    .replace(badlink,"<link $1></link>" )
                    .replace(badinput,"<input $1></input>" )
                    .replace(badmeta,"<meta $1></meta>" )
                    .replace(/<br>/g,"<br/>");

  // So far, so good! Next, extract converted text from page. <textarea name="text" ...>
  // Put the receieved xml response into XMLdocument format
  var doc = XmlService.parse(xml);

  var inlineHTML = getElementByVal( doc, 'textarea', 'name', 'text' );
  return (inlineHTML == null) ? null : inlineHTML.getValue();
}

Explanation

There may appear to be some black magic in there. A previous answer described how to use the old Xml Service to examine the structure of a web page to find the bits you wanted. It's still good reading (and voting up, hint, hint!), but that service is now gone, and the new XmlService doesn't have equivalent exploratory support.

To start, we found a web service that did the job we were interested in, and used the UrlFetch Service to simulate a person pasting code into the service. Ideally, we'd like one that returned just the result we wanted, in a format we could use without any further work. Alas, what we had was a complete web page, and that meant that we'd have to farm it for our result. Basic idea there: Use the XmlService to parse and explore the page, extracting just the bit we wanted.

In the Css Inline service that was selected, Chrome's ability to "Inspect Element" was used to determine the tag type (<textarea>) and a way to uniquely identify it (name="text"). Armed with that knowledge, we had everything we needed to use getElementByVal() to dig through the HTML returned from a POST request. (Alternatively, you could use String methods to find and extract the text you want.)

But when that was all put together, XmlService kept complaining about the format of the HTML in the result page - so JavaScript String & RegExp methods were used to balance the malformed tags before passing the page on.

Example

Here's a simple example illustrating use of the inline() function. Note that style information is absorbed from both the external css link AND the tagged styling.

function test_inline() {
  var myHtml =
    '<html>'
  +   '<head>'
  +     '<title>Example</title>'
  +     '<link rel="stylesheet" href="http://inlinestyler.torchboxapps.com/static/css/example.css" ></link>'
  +   '</head>'
  +   '<body>'
  +     '<style type="text/css">'
  +        'h1{'
  +             'color:yellow'
  +        '}'
  +     '</style>'
  +     '<h1>An example title</h1>'
  +     '<p>Paragraph 1</p>'
  +     '<p class="p2">Paragraph 2</p>'
  +   '</body>'
  + '</html>';

  var inlined = inline(myHtml);
  debugger;  // pause in debugger, have a look at "inlined"
}

Result

  <html>
    <head>
      <title>Example</title>
    </head>
    <body>
      <h1 style="color: yellow;">An example title</h1>
      <p style="margin: 0;padding: 0 0 10px 0;">Paragraph 1</p>
      <p class="p2" style="margin: 0;padding: 0 0 10px 0;">Paragraph 2</p>
    </body>
  </html>