The following CSS affects whether a page prints in portrait or landscape by default.
@page {
size: landscape;
}
I realize that this only works on a very limited set of browsers, and that the user can override it. That's okay; I'm just trying to provide a good default.
This works fine as a static value in CSS, but I'd like to switch dynamically between portrait & landscape based on on user choices. Is it possible to use JavaScript to change this value?
One simple way is to create a separate style for @page and change it:
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');
@page
declaration from CSS @jasssonpet saved my butt with his answer. That being said, I changed it to be a little simpler (in my opinion).
** Not quite sure why it was done the way it was, but please educate me if you know. If it's just preference, then I'm sharing my code sample because someone else's preferences might align better with mine.
// 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');
EDIT: I logged in for the first time in a while and saw @jasssonpet's answer and it makes a lot more sense to me now. In case anyone else is confused, the main benefit of his approach is 2 things:
Here is it in Class form (which I find easier to understand):
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 + "}");
}
}
Thanks @jasssonpet for his solution!
I ran into a problem that Vue JS can't v-bind
to css
outside of root
, like this
@media print {
@page {
size: A4 v-bind(printSize); // <-- doesn't work!
}
}
I need to manage orientation & margins independently, maybe more rules at future. Therefore, I rewrote and updated that code for embedding in other code and frameworks.
at 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 using example:
<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
}
}