function createElement(tagName, attrs, parentNode, doBindEvents) {
  var element = document.createElement(tagName);

  if (doBindEvents) {
    bindEvents(element);
    if (element.tagName == "IFRAME" && !('srcdoc' in attrs || 'src' in attrs)) {
      // If we're loading a frame, ensure we spin the event loop after load to
      // paper over the different event timing in Gecko vs Blink/WebKit
      // see https://github.com/whatwg/html/issues/4965
      element.eventPromise = element.eventPromise.then(() => {
        return new Promise(resolve => setTimeout(resolve, 0))
      });
    }
  }
  // We set the attributes after binding to events to catch any
  // event-triggering attribute changes. E.g. form submission.
  //
  // But be careful with images: unlike other elements they will start the load
  // as soon as the attr is set, even if not in the document yet, and sometimes
  // complete it synchronously, so the append doesn't have the effect we want.
  // So for images, we want to set the attrs after appending, whereas for other
  // elements we want to do it before appending.
  var isImg = (tagName == "img");
  if (!isImg)
    setAttributes(element, attrs);

  if (parentNode)
    parentNode.appendChild(element);

  if (isImg)
    setAttributes(element, attrs);

  return element;
}
Creates a new DOM element.
@param {string} tagName The type of the DOM element.
@param {object} attrs A JSON with attributes to apply to the element.
@param {DOMElement} parent Optional - an existing DOM element to append to
    If not provided, the returned element will remain orphaned.
@param {boolean} doBindEvents Optional - Whether to bind to load and error
    events and provide the promise wrapping the events via the element's
    {@code eventPromise} property. Default value evaluates to false.
@return {DOMElement} The newly created DOM element.