constructor(parentContainer, inputType, containerType, extraType, onChange) {
    /** @private {!Element} */
    this.container_ = document.createElement(containerType);
    parentContainer.latestElementContainer.appendChild(this.container_);

    /** @private {!HTMLInputElement} */
    this.input_ =
      /** @type {!HTMLInputElement} */(document.createElement(inputType));
    this.input_.onchange = () => {
      onChange(this.input_, this);
    };
    // <textarea> elements need to also react to 'input' events.
    if (inputType == 'textarea') {
      this.input_.oninput = () => {
        onChange(this.input_, this);
      };
    }
    this.input_.id = shakaDemo.Input.generateNewId_('input');
    this.container_.appendChild(this.input_);

    if (parentContainer.latestTooltip) {
      // Since the row isn't focusable, add the tooltip information into the
      // accessibility data of the input, so it can be accessed by users using
      // screen readers.
      const extraInfo = document.createElement('span');
      extraInfo.textContent = parentContainer.latestTooltip;
      extraInfo.classList.add('hidden');
      extraInfo.id = shakaDemo.Input.generateNewId_('extra-info');
      this.container_.appendChild(extraInfo);
      this.input_.setAttribute('aria-describedby', extraInfo.id);
    }

    /**
     * Most MDL inputs require some sort of "sibling element" that exists at
     * the same level as the input itself. These other elements are used to
     * create various visual effects, such as the ripple effect.
     * @private {?Element}
     */
    this.extra_ = null;
    if (extraType) {
      this.extra_ = document.createElement(extraType);
      this.container_.appendChild(this.extra_);
    }
  }
@param {!shakaDemo.InputContainer} parentContainer
@param {string} inputType The element type for the input object.
@param {string} containerType The element type for the container containing
  the input object.
@param {string} extraType The element type for the "sibling element" to the
  input object. If null, it adds no such element.
@param {function(!HTMLInputElement, !shakaDemo.Input)} onChange