Skip to content Skip to sidebar Skip to footer

How To Print A Specific Element In Vue 3?

I'm working on a project where I want the functionallity to print a specific element of my page. There is a mixin/plugin called VueHtmlToPaper that does exactly what I want but I h

Solution 1:

Since this plugin is not compatible with vue 3, we could do our plugin based on vue-html-to-paper plugin :

  1. create a folder called plugins in project root then inside it add VueHtmlToPaper.js file with the following content :
functionaddStyles(win, styles) {
    styles.forEach((style) => {
      let link = win.document.createElement("link");
      link.setAttribute("rel", "stylesheet");
      link.setAttribute("type", "text/css");
      link.setAttribute("href", style);
      win.document.getElementsByTagName("head")[0].appendChild(link);
    });
  }
  
  constVueHtmlToPaper = {
    install(app, options = {}) {
      app.config.globalProperties.$htmlToPaper = (
        el,
        localOptions,
        cb = () => true) => {
        let defaultName = "_blank",
          defaultSpecs = ["fullscreen=yes", "titlebar=yes", "scrollbars=yes"],
          defaultReplace = true,
          defaultStyles = [];
        let {
          name = defaultName,
          specs = defaultSpecs,
          replace = defaultReplace,
          styles = defaultStyles
        } = options;
  
        // If has localOptions// TODO: improve logicif (!!localOptions) {
          if (localOptions.name) name = localOptions.name;
          if (localOptions.specs) specs = localOptions.specs;
          if (localOptions.replace) replace = localOptions.replace;
          if (localOptions.styles) styles = localOptions.styles;
        }
  
        specs = !!specs.length ? specs.join(",") : "";
  
        const element = window.document.getElementById(el);
  
        if (!element) {
          alert(`Element to print #${el} not found!`);
          return;
        }
  
        const url = "";
        const win = window.open(url, name, specs, replace);
  
        win.document.write(`
          <html>
            <head>
              <title>${window.document.title}</title>
            </head>
            <body>
              ${element.innerHTML}
            </body>
          </html>
        `);
  
        addStyles(win, styles);
  
        setTimeout(() => {
          win.document.close();
          win.focus();
          win.print();
          win.close();
          cb();
        }, 1000);
  
        returntrue;
      };
    }
  };
  
  exportdefaultVueHtmlToPaper;
  

I just copied/pasted this code and I replaced Vue by app, then import it in main.js :

import { createApp } from'vue'importAppfrom'./App.vue'importVueHtmlToPaperfrom'./plugins/VueHtmlToPaper'let app=createApp(App);

 app.use(VueHtmlToPaper)

 app.mount('#app')

then use it in any component like :

<template><divclass="home"><imgalt="Vue logo"src="../assets/logo.png"><!-- SOURCE --><divid="printMe"><h1>Print me!</h1></div><!-- OUTPUT --><button @click="print">print</button></div></template><scriptlang="ts">import {
    defineComponent
} from'vue';
importHelloWorldfrom'@/components/HelloWorld.vue'; /

exportdefaultdefineComponent({
    name: 'Home',
    components: {
        HelloWorld,
    },
    methods: {
        print() {
            this.$htmlToPaper('printMe')
        }
    },
    mounted() {

    }
});
</script>

LIVE DEMO

Solution 2:

To prevent additional window, I replace window with the iframe inside VueHtmlToPaper.js

functionaddStyles(win, styles) {
    styles.forEach((style) => {
      let link = win.document.createElement("link");
      link.setAttribute("rel", "stylesheet");
      link.setAttribute("type", "text/css");
      link.setAttribute("href", style);
      win.document.getElementsByTagName("head")[0].appendChild(link);
    });
  }
  
  constVueHtmlToPaper = {
    install(app, options = {}) {
      app.config.globalProperties.$htmlToPaper = (
        el,
        localOptions,
        cb = () => true) => {
        let 
          defaultStyles = [];
        let {
          styles = defaultStyles
        } = options;
  
        // If has localOptions// TODO: improve logicif (localOptions) {
          if (localOptions.styles) styles = localOptions.styles;
        }
  
        const element = window.document.getElementById(el);
  
        if (!element) {
          alert(`Element to print #${el} not found!`);
          return;
        }
  
        var ifprint = document.createElement("iframe");
        document.body.appendChild(ifprint);
        ifprint.setAttribute("style","height:0;width:0;");

        const win = ifprint.contentWindow;
  
        win.document.write(`
          <html>
            <head>
              <title>${window.document.title}</title>
            </head>
            <body>
              ${element.innerHTML}
            </body>
          </html>
        `);
  
        addStyles(win, styles);
        
  
        setTimeout(() => {
          win.document.close();
          win.focus();
          win.print();
          win.close();
          document.body.removeChild(ifprint);
          cb();
        }, 1);
  
        returntrue;
      };
    }
  };
  
  exportdefaultVueHtmlToPaper;

Solution 3:

Did you manage to use a local css-file in the options? I can't get it done somehow. Something in the component like

methods: {
  print: function() {
  
    const printOptions = {
      name: '_blank',
      specs: [
        'fullscreen=yes',
        'titlebar=yes',
        'scrollbars=yes'
      ],
      styles: [
        'path/to/my/local/bulma.css',
        'path/to/some/custom/print.css'//I tried the correct path from the component to the css as well as something like '@/assets/print.css'
      ]
    }

    this.$htmlToPaper('app', printOptions, () => {
      console.log('Printing finished');
      // whatever
    });
  }
}

Post a Comment for "How To Print A Specific Element In Vue 3?"