javascript 键入“HTML集合|undefined“必须具有返回迭代器的"[Symbol.iterator]()”方法

wkftcu5l  于 2023-02-28  发布在  Java
关注(0)|答案(1)|浏览(375)

我不知道我在这里错过了什么。我正在https://www.youtube.com/watch?v=g1Zd0Y7OJuI&t=723s中学习使用Web组件构建自定义日期选择器的教程,并将JavaScript逻辑转换为Typescript。我遇到了connectedCallback() {...}中的一行,它抛出了以下错误:Type 'HTMLCollection | undefined' must have a '[Symbol.iterator]()' method that returns an iterator on line const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(".calendar-header")?.children;我在Stackoverflow上看过类似的问题,但发现他们建议的解决方案对我不起作用。请帮助我了解我需要考虑什么来消除这个可怕的错误!

class DatePicker extends HTMLElement {
  shadow: ShadowRoot;
  calendar: Calendar;
  mounted: boolean = false;

  /** Elements */
  calendarDropdown: Element | null = null;
  calendarDateElement: HTMLHeadingElement | null | undefined = null;

  constructor() {
    super();
    ...
}

  connectedCallback() {
    this.mounted = true;

    this.toggleButton = this.shadow.querySelector(".date-toggle");
    this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
    const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(".calendar-header")?.children; // <--- This is the line complain 
    this.calendarDateElement = calendarDateElement;

    this.toggleButton?.addEventListener("click", () => this.toggleCalendar());
    prevBtn.addEventListener("click", () => this.prevMonth());
    nextBtn.addEventListener("click", () => this.nextMonth());
  }
}
gpnt7bae

gpnt7bae1#

我查找了这个资源https://www.javascripttutorial.net/javascript-dom/javascript-get-child-element/,找到了一个解决问题的方法,但我希望有一个更好的解决方案。我没有像const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(".calendar-header")?.children;这样重构父节点的子节点,而是找到了每个节点,并将其分别赋给一个变量,如以下代码所示:

connectedCallback() {
...
   this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
   const prevBtn = this.shadow.querySelector(".previous-month");
   const calendarDateElement =
     this.shadow.querySelector(".previous-month")?.nextElementSibling;
   const nextBtn = this.shadow.querySelector(".next-month");
   this.calendarDateElement = calendarDateElement;

   ...
   prevBtn?.addEventListener("click", () => this.prevMonth());
   nextBtn?.addEventListener("click", () => this.nextMonth());
}

下面是整个类:

import { Calendar, Day } from ".";

class DatePicker extends HTMLElement {
  date: any = null;
  format = "MMM DD, YYYY";
  position: string | null = "bottom";
  visible: boolean | undefined = false;
  shadow: ShadowRoot;
  calendar: Calendar;
  mounted: boolean = false;

  /** Elements */
  toggleButton: HTMLButtonElement | null = null;
  calendarDropdown: Element | null = null;
  calendarDateElement: ChildNode | null | undefined = null;

  constructor() {
    super();

    const lang = window.navigator.language;
    const date = new Date(
      this.date ?? (this.getAttribute("date") || Date.now())
    );

    this.shadow = this.attachShadow({ mode: "open" });
    this.date = new Day(date);
    this.calendar = new Calendar(this.date.year, this.date.monthNumber, lang);

    this.format = this.getAttribute("format") || this.format;
    this.position = DatePicker.position.includes(
      this.getAttribute("position") as string
    )
      ? this.getAttribute("position")
      : this.position;

    this.visible =
      this.getAttribute("visible") === "" ||
      this.getAttribute("visible") === "true" ||
      this.visible;

    this.render();
  }

  connectedCallback() {
    this.mounted = true;

    this.toggleButton = this.shadow.querySelector(".date-toggle");
    this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
    const prevBtn = this.shadow.querySelector(".previous-month");
    const calendarDateElement =
      this.shadow.querySelector(".previous-month")?.nextElementSibling;
    const nextBtn = this.shadow.querySelector(".next-month");
    this.calendarDateElement = calendarDateElement;

    this.toggleButton?.addEventListener("click", () => this.toggleCalendar());
    prevBtn?.addEventListener("click", () => this.prevMonth());
    nextBtn?.addEventListener("click", () => this.nextMonth());
  }

  // = this.calendarDropdown.sec
  //this.calendarDropdown?.querySelector(".calendar-header")?.children;

  prevMonth() {
    console.log("Prev Clicked");
    this.calendar.goToPreviousMonth();
    this.updateCalendarHeaderText();
  }
  nextMonth() {
    console.log("Next Clicked");
    this.calendar.goToNextMonth();
    this.updateCalendarHeaderText();
  }

  updateCalendarHeaderText() {
    if (this.calendarDateElement)
      this.calendarDateElement.textContent = `${this.calendar.month.name}, ${this.calendar.year}`;
  }

  toggleCalendar(visible: boolean | null = null) {
    visible === null
      ? this.calendarDropdown?.classList.toggle("visible")
      : visible
      ? this.calendarDropdown?.classList.add("visible")
      : this.calendarDropdown?.classList.remove("visible");

    this.visible = this.calendarDropdown?.className.includes("visible");
  }

  static get position() {
    return ["top", "right", "bottom", "left"];
  }

  get styles() {
    return `
      :host {
        position: relative;
      }

      .date-toggle {
        background: #eee;
        border: none;
        border-radius: 0.5em;
        color: var(--teal);
        cursor: pointer;
        font-size: medium;
        font-weight: lighter;
        margin: 1em 0;
        padding: 1.1em;
        text-transform: capitalize;
        width: 100%;
      }

      .calendar-dropdown {
        background: #008080;
        border-radius: 0.5em;
        box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
        color: var(--white);
        display: block;
        // height: 300px;
        left: 50%;
        min-width: 200px;
        padding: 20px;
        position: absolute;
        top: 100%;
        transform: translate(-52%, 15px);
        width: 95%;
        z-index: 3;
      }

      .calendar-dropdown.visible {
        display: block;
      }

      .calendar-header {
        display: flex;
        justify-content: space-between;
        align-items: center;
        margin: 10px 0 30px;
      }

      .calendar-header h4 {
        margin: 0;
        font-size: 21px;
        font-weight: lighter;
        text-transform: capitalize;
      }

      .calendar-header button {
        background: none;
        border: 8px solid transparent;
        border-radius: 0.1em;
        border-top-color: var(--white);
        cursor: pointer;
        height: 0;
        padding: 0;
        position: relative;
        transform: rotate(90deg);
        width: 0;
      }

      .calendar-header button::after {
        content: '';
        display: block;
        height: 25px;
        left: 50%;
        position: absolute;
        top: 50%;
        transform: translate(-50%, -50%);
        width: 25px;
      }

      .calendar-header button:last-of-type {
        transform: rotate(-90deg);
      }
    `;
  }

  render() {
    const monthYear = `${this.calendar.month.name}, ${this.calendar.year}`;
    const date = this.date.format(this.format);
    this.shadow.innerHTML = `
    <style>${this.styles}</style>
    <button type="button" class="date-toggle">${date}</button>
    <div class="calendar-dropdown ${this.visible ? "visible" : ""} 
    ${this.position}">
      <div class="calendar-header">
        <button aria-label="previous month" class="previous-month" type="button"></button>
        <h4 class="month-year">
            ${monthYear}
          </h4>
        <button aria-label="next month" class="next-month" type="button"></button>
      </div>
    </div>
    `;
  }
}

export default DatePicker;

window.customElements.get("date-picker") ||
  window.customElements.define("date-picker", DatePicker);

相关问题