JavaScript 可以改变@page CSS 的值吗?

Can JavaScript change the value of @page CSS?

本文关键字:CSS @page 改变 JavaScript      更新时间:2023-09-26

以下 CSS 会影响默认情况下页面是纵向打印还是横向打印。

@page {
   size: landscape;
}

我意识到这仅适用于一组非常有限的浏览器,并且用户可以覆盖它。 没关系;我只是想提供一个好的默认值。

这在 CSS 中作为静态值工作得很好,但我想根据用户选择在纵向和横向之间动态切换。 是否可以使用 JavaScript 来更改此值?

一种简单的方法是为@page创建一个单独的样式并进行更改:

var cssPagedMedia = (function () {
    var style = document.createElement('style');
    document.head.appendChild(style);
    return function (rule) {
        style.innerHTML = rule;
    };
}());
cssPagedMedia.size = function (size) {
    cssPagedMedia('@page {size: ' + size + '}');
};
cssPagedMedia.size('landscape');
@jasssonpet用

他的答案拯救了我的屁股。话虽如此,我将其更改为更简单(在我看来)。

** 不太确定为什么会这样做,但如果你知道,请教育我。如果只是偏好,那么我正在分享我的代码示例,因为其他人的偏好可能与我的更一致。

// just create the style tag and set the page size
function setPageSize(cssPageSize) {
    const style = document.createElement('style');
    style.innerHTML = `@page {size: ${cssPageSize}}`;
    document.head.appendChild(style);
}
// how to use it
setPageSize('letter landscape');

编辑:我有一段时间第一次登录,看到了@jasssonpet的答案,现在对我来说更有意义了。如果其他人感到困惑,他的方法的主要好处是两件事:

  1. 他使用闭包,因此只有 1 个样式标签添加到页面,然后可以引用和更改。
  2. 他扩展了cssPagedMedia,以便添加的任何方法都是命名空间。这样,全局命名空间就不会受到污染。

这是类形式(我觉得更容易理解):

class SingletonStyle {
  constructor() {
    this.style = document.createElement("style");
    document.head.appendChild(this.style);
  }
  apply(rule) {
    this.style.innerHTML = rule;
  }
  size(size) {
    this.apply("@page {size: " + size + "}");
  }
}

感谢@jasssonpet的解决方案!我遇到了一个 Vue JS 无法v-bind css root之外的问题,就像这样

@media print {
  @page {
    size: A4 v-bind(printSize); // <-- doesn't work!
  }
}

我需要独立管理方向和利润,也许将来会有更多的规则。因此,我重写并更新了该代码以嵌入其他代码和框架中。

utils/customizePageStyle.js

let pageStyle;
const pageRules = {};
const pageHasRules = () => !!Object.keys(pageRules).length;
const addCustomCssStylesheet = async () => {
  pageStyle = document.createElement('style');
  document.head.appendChild(pageStyle);
};
export const cleanPageRules = () => pageStyle.innerHTML = `@page {}`;
const writePageRules = async () => {
  if (!pageStyle) await addCustomCssStylesheet();
  if (!pageHasRules) {
    cleanPageRules();
    return;
  }
  let styleTxt = '@media print { @page {';
  Object.keys(pageRules).forEach(ruleName => {
    const ruleTxt = pageRules[ruleName];
    styleTxt += `${ruleName}: ${ruleTxt} !important; `;
  });
  styleTxt += '} } ';
  pageStyle.innerHTML = styleTxt;
};
const addPageRule = ({ruleName, ruleValue}) => pageRules[ruleName] = ruleValue;
const asyncAddPageRule = async rule => {
  addPageRule(rule);
  await writePageRules();
};
const addPageRules = async rules => {
  rules.forEach(rule => addPageRule(rule));
  await writePageRules();
};
const delPageRule = ruleName => pageRules[ruleName] = null;
const asyncDelPageRule = async ruleName => {
  delPageRule(ruleName);
  await writePageRules();
};
const delPageRules = async ruleNames => {
  ruleNames.forEach(ruleName => delPageRule(ruleName));
  pageHasRules ? await writePageRules() : cleanPageRules();
};
export const setPageRules = {
  add: asyncAddPageRule,
  adds: addPageRules,
  del: asyncDelPageRule,
  dels: delPageRules,
  clean: cleanPageRules
};

Vue Js 使用示例:

<script setup>
import { setPageRules } from '@/utils/customizePageStyle.js';
const printSize = computed(()=> yourCondition1 ? 'A4 portrait' : 'A4 landscape');
const printMargin = computed(()=> yourCondition2 ?
  '1cm 1cm 1cm 2cm' : '24pt 24pt auto 18pt');
onMounted(()=> {
  window.addEventListener('beforeprint', async e => {
    await setPageRules.adds([
      {ruleName: 'size', ruleValue: printSize.value},
      {ruleName: 'margin', ruleValue: printMargin.value},
    ]);
  });
  window.addEventListener('afterprint', e => setPageRules.clean());
}
</script>
<style>
@media print {
  body {
    margin: 0px; // don't work with it, I don't know why
  }
}