Skip to content

Suggestion: Add a simple check for node existence before removing #218

@ekatrif

Description

@ekatrif

I'm using the library (v.3.7.2) to create the image from HTML element and export it to PDF file.

    const elementToPrint =
      iframe.contentWindow.document.querySelector('.my-element-to-print');

    const dataUrl = await domToImage.toJpeg(elementToPrint, {
      quality: 1,
      ...other props,
    });

Everithing works fine, I'm getting my beautiful PDF, but an error occurs:

Uncaught NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node.
    at o.onload (VM49568 dom-to-image-more.min.js:2:3035)
o.onload @ dom-to-image-more.min.js:2

Error occurs in makeImage function when onLoad event is happening:

  function makeImage(uri) {
            if (uri === 'data:,') {
                return Promise.resolve();
            }
            return new Promise(function (resolve, reject) {
                // Create an SVG element to house the image
                const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');

                // and create the Image element to insert into that wrapper
                const image = new Image();

                if (domtoimage.impl.options.useCredentials) {
                    image.crossOrigin = 'use-credentials';
                }
                image.onload = function () {
                    // Cleanup: remove theimage from the document
                    document.body.removeChild(svg); // !!!! Error occurs here !!!!
                    if (window && window.requestAnimationFrame) {
                        // In order to work around a Firefox bug (webcompat/web-bugs#119834) we
                        // need to wait one extra frame before it's safe to read the image data.
                        window.requestAnimationFrame(function () {
                            resolve(image);
                        });
                    } else {
                        // If we don't have a window or requestAnimationFrame function proceed immediately.
                        resolve(image);
                    }
                };
                image.onerror = (error) => {
                    // Cleanup: remove the image from the document
                    document.body.removeChild(svg);

                    reject(error);
                };
                svg.appendChild(image);
                image.src = uri;
                // Add the SVG to the document body (invisible)
                document.body.appendChild(svg);
            });
        }

I managed to get rid of the error by adding a patch to this function:

    import domToImage from 'dom-to-image-more';

    const patchedDomToImage = patchDomToImage(domToImage);

    const dataUrl = await patchedDomToImage.toJpeg(elementToPrint, {
      quality: 1,
      ...other props,
    });

In patch function I added a simple check, if parentNode of 'svg' is document.body:

function patchDomToImage(domToImage: any) {
  if (domToImage && !domToImage.patched) {
    domToImage.impl.util.makeImage = (uri: string) => {
      if (uri === 'data:,') {
        return Promise.resolve();
      }
      return new Promise(function (resolve, reject) {
        const svg = document.createElementNS(
          'http://www.w3.org/2000/svg',
          'svg',
        );
        const image = new Image();
        if (domToImage.impl.options.useCredentials) {
          image.crossOrigin = 'use-credentials';
        }
        image.onload = function () {
          // If parent svg exists in document.body
          // Remove it
          if (svg.parentNode === document.body) {
            document.body.removeChild(svg);
          }
          if (window?.requestAnimationFrame) {
            window.requestAnimationFrame(function () {
              resolve(image);
            });
          } else {
            resolve(image);
          }
        };
        image.onerror = error => {
          // If parent svg exists in document.body
          // Remove it
          if (svg.parentNode === document.body) {
            document.body.removeChild(svg);
          }
          reject(error);
        };
        svg.appendChild(image);
        image.src = uri;
        document.body.appendChild(svg);
      });
    };
    domToImage.patched = true;
  }
  return domToImage;
}

Now everything works fine with no errors. I don't know the exact reason why the script doesn't find the SVG node, perhaps the garbage collector manages to delete it.
It would be interesting to hear your ideas about the reasons for this error.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions