MatthewHerbst / react-to-print

Print React components in the browser. Supports Chrome, Safari, Firefox and EDGE
MIT License
2.06k stars 221 forks source link

HTML doctype declaration is displaying on top of the print dialog window #708

Closed john-pellegrini closed 4 days ago

john-pellegrini commented 3 months ago

Hi, I'm getting the <!DOCTYPE html> declaration included in the print content: image Any ideas on what might be causing this to happen? Thank you for your help

MatthewHerbst commented 3 months ago

Hey there. Could you share your setup please (a Codesandbox or similar would be ideal)? We do inject the doctype into the HTML of the iframe being printed, but not sure how it could be actually displaying for you

john-pellegrini commented 3 months ago

Actually when I'm running/developing the react app on my local machine I don't see the "doctype" in the print dialog window. This only happened when the code was pushed to our test servers. I'm happy to share what ever you think could be helpful, but it's a very large react app so when you say "setup" what specifically would help you? thank you This is the component where I've added the useReactToPrint hook: viewScreen.txt

MatthewHerbst commented 3 months ago

Thanks! Do you know if your custom print function is getting used in this case? If so, could you share the customToPrint function's implementation?

john-pellegrini commented 3 months ago

I'm using the "custom" print function in one of the pages I'm implementing the print functionality, however it shows the doc type in the other 2 as well. This is my custom function:

    const customToPrint = (printWindow: HTMLIFrameElement): Promise<void> => {
        const printContent = printWindow.contentDocument || printWindow.contentWindow?.document;
        const printedScrollContainerList = printContent?.querySelectorAll('.MuiDataGrid-virtualScroller');
        const originScrollContainerList = document.querySelectorAll('.MuiDataGrid-virtualScroller');

        if (!printedScrollContainerList || !originScrollContainerList) {
            return Promise.resolve();
        }

        const getScrollHeaderText = (scrollContainer: Element): string | null | undefined =>
            scrollContainer
                .closest('div[data-testid="layout-gadget-grid"]')
                ?.querySelector('div[class*="-header"] > span')?.textContent;

        const originScrollContainerRecords: Record<string, Element> = Array.from(originScrollContainerList).reduce(
            (acc, scroll) => {
                const scrollHeaderText = getScrollHeaderText(scroll);
                scrollHeaderText && (acc[scrollHeaderText] = scroll);
                return acc;
            },
            {},
        );

        Array.from(printedScrollContainerList).forEach((printedScrollContainer) => {
            const scrollHeaderText = getScrollHeaderText(printedScrollContainer);
            scrollHeaderText &&
                printedScrollContainer?.scrollTo(0, Number(originScrollContainerRecords[scrollHeaderText]?.scrollTop));
        });

        printWindow?.contentWindow?.print();

        // print must return a Promise
        return Promise.resolve();
    };
john-pellegrini commented 2 months ago

@MatthewHerbst - if you have any idea how the doctype declaration could be added to the body tag please let me know? image Thanks

MatthewHerbst commented 2 months ago

I'm truly not sure. I've tested the code we run and in Chrome 126 on my machine it does not do that:

const printWindow = document.createElement("iframe");
printWindow.srcdoc = "<!DOCTYPE html>";

which gives

Screenshot 2024-06-18 at 10 13 52 AM

What stands out to me looking at your code in the image above is that it seems the srcdoc property on your iframe isn't correct. It has srcdoc="&lt;!DOCTYPE html&gt;" when it should be srcdoc="<!DOCTYPE html>"

Do you have some sort of safety encoding going on or something? &lt; is the code for < and &gt; is the code for >.

john-pellegrini commented 2 months ago

Ok thank you for checking - it's working fine for me locally as well, but once the code gets pushed to our test servers this issue occurs. Like you said it seems there's some additional encoding occurring for some reason. (frustrating) Another quick question? Is there a way to access the print window other then in the "print" api function? I'm trying to come up with a "hack" to prevent this but I don't want to create a "custom" print function for every component

MatthewHerbst commented 2 months ago

Is there a way to access the print window other then in the "print" api function?

If you ensure that removeAfterPrint: false is set on the printing options then after the print is run the printing iframe (with ID of "printWindow") should still be in your DOM and you can inspect it directly. The iframe will be replaced the next time you click print.

(In v3 of this lib which I'm working on we remove it by default and preserving it is opt-in, but for now it's preserved by default since that's been the behavior for years and changing that would be a breaking change)

MatthewHerbst commented 2 months ago

Regarding the encoding, do you have a script on your page maybe that's trying to make sure all added code has tags removed to prevent execution? For example, you might be trying to prevent folks from injecting <script>badFunction()</script> type things, though I've never seen something be so encompassing as to detect setting a random property on a runtime-only iframe

john-pellegrini commented 2 months ago

@MatthewHerbst - thank you again for your responses. I'm checking with others to see if there aware of anything that might be causing this to happen

MatthewHerbst commented 1 week ago

@john-pellegrini did you ever get to the bottom of this issue?

john-pellegrini commented 5 days ago

Unfortunately we weren't able to figure why this was happening, so I'm using onBeforePrint() callback function to remove the node from the DOM

MatthewHerbst commented 4 days ago

Thanks for the update! Will close this for now, please feel free to re-open if there's something more you think react-to-print can do to help here