Type 'HTMLCollection | undefined' must have a '[Symbol.iterator]()' method that returns an iterator

huangapple go评论98阅读模式
英文:

Type 'HTMLCollection | undefined' must have a '[Symbol.iterator]()' method that returns an iterator

问题

这是您提供的代码的中文翻译:

  1. class DatePicker extends HTMLElement {
  2. shadow: ShadowRoot;
  3. calendar: Calendar;
  4. mounted: boolean = false;
  5. /** 元素 */
  6. calendarDropdown: Element | null = null;
  7. calendarDateElement: HTMLHeadingElement | null | undefined = null;
  8. constructor() {
  9. super();
  10. // ...
  11. }
  12. connectedCallback() {
  13. this.mounted = true;
  14. this.toggleButton = this.shadow.querySelector(".date-toggle");
  15. this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
  16. const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(".calendar-header")?.children; // <--- 这是引发错误的那一行
  17. this.calendarDateElement = calendarDateElement;
  18. this.toggleButton?.addEventListener("click", () => this.toggleCalendar());
  19. prevBtn.addEventListener("click", () => this.prevMonth());
  20. nextBtn.addEventListener("click", () => this.nextMonth());
  21. }
  22. }

请注意,我已经将代码中的HTML实体字符(如")替换为正常的引号和标记。

英文:

I don't know what I am missing out here. I am following a tutorial in building a custom date-picker using a web component here https://www.youtube.com/watch?v=g1Zd0Y7OJuI&t=723s and am translating the JavaScript logic to Typescript. I have reached a line inside connectedCallback() {...} that throws the following error: Type &#39;HTMLCollection | undefined&#39; must have a &#39;[Symbol.iterator]()&#39; method that returns an iterator on the line const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(&quot;.calendar-header&quot;)?.children; I have looked at similar questions here on Stackoverflow but found that their suggested solutions are not working for me. Kindly assist me in understanding what I need to take into account to eliminate this horrific error!

  1. class DatePicker extends HTMLElement {
  2. shadow: ShadowRoot;
  3. calendar: Calendar;
  4. mounted: boolean = false;
  5. /** Elements */
  6. calendarDropdown: Element | null = null;
  7. calendarDateElement: HTMLHeadingElement | null | undefined = null;
  8. constructor() {
  9. super();
  10. ...
  11. }
  12. connectedCallback() {
  13. this.mounted = true;
  14. this.toggleButton = this.shadow.querySelector(&quot;.date-toggle&quot;);
  15. this.calendarDropdown = this.shadow.querySelector(&quot;.calendar-dropdown&quot;);
  16. const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(&quot;.calendar-header&quot;)?.children; // &lt;--- This is the line complain
  17. this.calendarDateElement = calendarDateElement;
  18. this.toggleButton?.addEventListener(&quot;click&quot;, () =&gt; this.toggleCalendar());
  19. prevBtn.addEventListener(&quot;click&quot;, () =&gt; this.prevMonth());
  20. nextBtn.addEventListener(&quot;click&quot;, () =&gt; this.nextMonth());
  21. }
  22. }

答案1

得分: 0

I looked up this resource https://www.javascripttutorial.net/javascript-dom/javascript-get-child-element/ and found a workaround for my problem but I would like an even better solution to this issue. Instead of destructuring child nodes of the parent node like this const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(".calendar-header")?.children; I found each node and assigned it to a variable separately as in the following code:

  1. connectedCallback() {
  2. ...
  3. this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
  4. const prevBtn = this.shadow.querySelector(".previous-month");
  5. const calendarDateElement =
  6. this.shadow.querySelector(".previous-month")?.nextElementSibling;
  7. const nextBtn = this.shadow.querySelector(".next-month");
  8. this.calendarDateElement = calendarDateElement;
  9. ...
  10. prevBtn?.addEventListener("click", () => this.prevMonth());
  11. nextBtn?.addEventListener("click", () => this.nextMonth());
  12. }

Here below is the entire class:

  1. import { Calendar, Day } from ".";
  2. class DatePicker extends HTMLElement {
  3. date: any = null;
  4. format = "MMM DD, YYYY";
  5. position: string | null = "bottom";
  6. visible: boolean | undefined = false;
  7. shadow: ShadowRoot;
  8. calendar: Calendar;
  9. mounted: boolean = false;
  10. /** Elements */
  11. toggleButton: HTMLButtonElement | null = null;
  12. calendarDropdown: Element | null = null;
  13. calendarDateElement: ChildNode | null | undefined = null;
  14. constructor() {
  15. super();
  16. const lang = window.navigator.language;
  17. const date = new Date(
  18. this.date ?? (this.getAttribute("date") || Date.now())
  19. );
  20. this.shadow = this attachShadow({ mode: "open" });
  21. this.date = new Day(date);
  22. this.calendar = new Calendar(this.date.year, this.date.monthNumber, lang);
  23. this.format = this.getAttribute("format") || this.format;
  24. this.position = DatePicker.position.includes(
  25. this.getAttribute("position") as string
  26. )
  27. ? this.getAttribute("position")
  28. : this.position;
  29. this.visible =
  30. this.getAttribute("visible") === "" ||
  31. this.getAttribute("visible") === "true" ||
  32. this.visible;
  33. this.render();
  34. }
  35. connectedCallback() {
  36. this.mounted = true;
  37. this.toggleButton = this.shadow.querySelector(".date-toggle");
  38. this.calendarDropdown = this.shadow.querySelector(".calendar-dropdown");
  39. const prevBtn = this.shadow.querySelector(".previous-month");
  40. const calendarDateElement =
  41. this.shadow.querySelector(".previous-month")?.nextElementSibling;
  42. const nextBtn = this.shadow.querySelector(".next-month");
  43. this.calendarDateElement = calendarDateElement;
  44. this.toggleButton?.addEventListener("click", () => this.toggleCalendar());
  45. prevBtn?.addEventListener("click", () => this.prevMonth());
  46. nextBtn?.addEventListener("click", () => this.nextMonth());
  47. }
  48. // = this.calendarDropdown.sec
  49. //this.calendarDropdown?.querySelector(".calendar-header")?.children;
  50. prevMonth() {
  51. console.log("Prev Clicked");
  52. this.calendar.goToPreviousMonth();
  53. this.updateCalendarHeaderText();
  54. }
  55. nextMonth() {
  56. console.log("Next Clicked");
  57. this.calendar.goToNextMonth();
  58. this.updateCalendarHeaderText();
  59. }
  60. updateCalendarHeaderText() {
  61. if (this.calendarDateElement)
  62. this.calendarDateElement.textContent = `${this.calendar.month.name}, ${this.calendar.year}`;
  63. }
  64. toggleCalendar(visible: boolean | null = null) {
  65. visible === null
  66. ? this.calendarDropdown?.classList.toggle("visible")
  67. : visible
  68. ? this.calendarDropdown?.classList.add("visible")
  69. : this.calendarDropdown?.classList.remove("visible");
  70. this.visible = this.calendarDropdown?.className.includes("visible");
  71. }
  72. static get position() {
  73. return ["top", "right", "bottom", "left"];
  74. }
  75. get styles() {
  76. return `
  77. :host {
  78. position: relative;
  79. }
  80. .date-toggle {
  81. background: #eee;
  82. border: none;
  83. border-radius: 0.5em;
  84. color: var(--teal);
  85. cursor: pointer;
  86. font-size: medium;
  87. font-weight: lighter;
  88. margin: 1em 0;
  89. padding: 1.1em;
  90. text-transform: capitalize;
  91. width: 100%;
  92. }
  93. .calendar-dropdown {
  94. background: #008080;
  95. border-radius: 0.5em;
  96. box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
  97. color: var(--white);
  98. display: block;
  99. // height: 300px;
  100. left: 50%;
  101. min-width: 200px;
  102. padding: 20px;
  103. position: absolute;
  104. top: 100%;
  105. transform: translate(-52%, 15px);
  106. width: 95%;
  107. z-index: 3;
  108. }
  109. .calendar-dropdown.visible {
  110. display: block;
  111. }
  112. .calendar-header {
  113. display: flex;
  114. justify-content: space between;
  115. align-items: center;
  116. margin: 10px 0 30px;
  117. }
  118. .calendar-header h4 {
  119. margin: 0;
  120. font-size: 21px;
  121. font-weight: lighter;
  122. text-transform: capitalize;
  123. }
  124. .calendar-header button {
  125. background: none;
  126. border: 8px solid transparent;
  127. border-radius: 0.1em;
  128. border-top-color: var(--white);
  129. cursor: pointer;
  130. height: 0;
  131. padding: 0;
  132. position: relative;
  133. transform: rotate(90deg);
  134. width: 0;
  135. }
  136. .calendar-header button::after {
  137. content: '';
  138. display: block;
  139. height: 25px;
  140. left: 50%;
  141. position: absolute;
  142. top: 50%;
  143. transform: translate(-50%, -50%);
  144. width: 25px;
  145. }
  146. .calendar-header button:last-of-type {
  147. transform: rotate(-90deg);
  148. }
  149. `;
  150. }
  151. render() {
  152. const monthYear = `${this.calendar.month.name}, ${this.calendar.year}`;
  153. const date = this.date.format(this.format);
  154. this.shadow.innerHTML = `
  155. <style>${this.styles}</style>
  156. <button type="button" class="date-toggle">${date}</button>
  157. <div class="calendar-dropdown ${this.visible ? "visible" : ""}
  158. ${this.position}">
  159. <div class="calendar-header">
  160. <button aria-label="previous month" class="previous-month" type="button"></button>
  161. <h4 class="month-year">
  162. ${monthYear}
  163. </h4>
  164. <button aria-label="next month" class="next-month" type="button"></button>
  165. </div>
  166. </div>
  167. `;
  168. }
  169. }
  170. export default DatePicker;
  171. window.customElements.get("date-picker") ||
  172. window.customElements.define("date-picker", DatePicker);
英文:

I looked up this resource https://www.javascripttutorial.net/javascript-dom/javascript-get-child-element/ and found a workaround for my problem but I would like an even better solution to this issue. Instead of destructuring child nodes of the parent node like this const [prevBtn, calendarDateElement, nextBtn] = this.calendarDropdown?.querySelector(&quot;.calendar-header&quot;)?.children; I found each node and assigned it to a variable separately as in the following code:

  1. connectedCallback() {
  2. ...
  3. this.calendarDropdown = this.shadow.querySelector(&quot;.calendar-dropdown&quot;);
  4. const prevBtn = this.shadow.querySelector(&quot;.previous-month&quot;);
  5. const calendarDateElement =
  6. this.shadow.querySelector(&quot;.previous-month&quot;)?.nextElementSibling;
  7. const nextBtn = this.shadow.querySelector(&quot;.next-month&quot;);
  8. this.calendarDateElement = calendarDateElement;
  9. ...
  10. prevBtn?.addEventListener(&quot;click&quot;, () =&gt; this.prevMonth());
  11. nextBtn?.addEventListener(&quot;click&quot;, () =&gt; this.nextMonth());
  12. }

Here below is the entire class:

  1. import { Calendar, Day } from &quot;.&quot;;
  2. class DatePicker extends HTMLElement {
  3. date: any = null;
  4. format = &quot;MMM DD, YYYY&quot;;
  5. position: string | null = &quot;bottom&quot;;
  6. visible: boolean | undefined = false;
  7. shadow: ShadowRoot;
  8. calendar: Calendar;
  9. mounted: boolean = false;
  10. /** Elements */
  11. toggleButton: HTMLButtonElement | null = null;
  12. calendarDropdown: Element | null = null;
  13. calendarDateElement: ChildNode | null | undefined = null;
  14. constructor() {
  15. super();
  16. const lang = window.navigator.language;
  17. const date = new Date(
  18. this.date ?? (this.getAttribute(&quot;date&quot;) || Date.now())
  19. );
  20. this.shadow = this.attachShadow({ mode: &quot;open&quot; });
  21. this.date = new Day(date);
  22. this.calendar = new Calendar(this.date.year, this.date.monthNumber, lang);
  23. this.format = this.getAttribute(&quot;format&quot;) || this.format;
  24. this.position = DatePicker.position.includes(
  25. this.getAttribute(&quot;position&quot;) as string
  26. )
  27. ? this.getAttribute(&quot;position&quot;)
  28. : this.position;
  29. this.visible =
  30. this.getAttribute(&quot;visible&quot;) === &quot;&quot; ||
  31. this.getAttribute(&quot;visible&quot;) === &quot;true&quot; ||
  32. this.visible;
  33. this.render();
  34. }
  35. connectedCallback() {
  36. this.mounted = true;
  37. this.toggleButton = this.shadow.querySelector(&quot;.date-toggle&quot;);
  38. this.calendarDropdown = this.shadow.querySelector(&quot;.calendar-dropdown&quot;);
  39. const prevBtn = this.shadow.querySelector(&quot;.previous-month&quot;);
  40. const calendarDateElement =
  41. this.shadow.querySelector(&quot;.previous-month&quot;)?.nextElementSibling;
  42. const nextBtn = this.shadow.querySelector(&quot;.next-month&quot;);
  43. this.calendarDateElement = calendarDateElement;
  44. this.toggleButton?.addEventListener(&quot;click&quot;, () =&gt; this.toggleCalendar());
  45. prevBtn?.addEventListener(&quot;click&quot;, () =&gt; this.prevMonth());
  46. nextBtn?.addEventListener(&quot;click&quot;, () =&gt; this.nextMonth());
  47. }
  48. // = this.calendarDropdown.sec
  49. //this.calendarDropdown?.querySelector(&quot;.calendar-header&quot;)?.children;
  50. prevMonth() {
  51. console.log(&quot;Prev Clicked&quot;);
  52. this.calendar.goToPreviousMonth();
  53. this.updateCalendarHeaderText();
  54. }
  55. nextMonth() {
  56. console.log(&quot;Next Clicked&quot;);
  57. this.calendar.goToNextMonth();
  58. this.updateCalendarHeaderText();
  59. }
  60. updateCalendarHeaderText() {
  61. if (this.calendarDateElement)
  62. this.calendarDateElement.textContent = `${this.calendar.month.name}, ${this.calendar.year}`;
  63. }
  64. toggleCalendar(visible: boolean | null = null) {
  65. visible === null
  66. ? this.calendarDropdown?.classList.toggle(&quot;visible&quot;)
  67. : visible
  68. ? this.calendarDropdown?.classList.add(&quot;visible&quot;)
  69. : this.calendarDropdown?.classList.remove(&quot;visible&quot;);
  70. this.visible = this.calendarDropdown?.className.includes(&quot;visible&quot;);
  71. }
  72. static get position() {
  73. return [&quot;top&quot;, &quot;right&quot;, &quot;bottom&quot;, &quot;left&quot;];
  74. }
  75. get styles() {
  76. return `
  77. :host {
  78. position: relative;
  79. }
  80. .date-toggle {
  81. background: #eee;
  82. border: none;
  83. border-radius: 0.5em;
  84. color: var(--teal);
  85. cursor: pointer;
  86. font-size: medium;
  87. font-weight: lighter;
  88. margin: 1em 0;
  89. padding: 1.1em;
  90. text-transform: capitalize;
  91. width: 100%;
  92. }
  93. .calendar-dropdown {
  94. background: #008080;
  95. border-radius: 0.5em;
  96. box-shadow: 0 0 8px rgba(0, 0, 0, 0.2);
  97. color: var(--white);
  98. display: block;
  99. // height: 300px;
  100. left: 50%;
  101. min-width: 200px;
  102. padding: 20px;
  103. position: absolute;
  104. top: 100%;
  105. transform: translate(-52%, 15px);
  106. width: 95%;
  107. z-index: 3;
  108. }
  109. .calendar-dropdown.visible {
  110. display: block;
  111. }
  112. .calendar-header {
  113. display: flex;
  114. justify-content: space-between;
  115. align-items: center;
  116. margin: 10px 0 30px;
  117. }
  118. .calendar-header h4 {
  119. margin: 0;
  120. font-size: 21px;
  121. font-weight: lighter;
  122. text-transform: capitalize;
  123. }
  124. .calendar-header button {
  125. background: none;
  126. border: 8px solid transparent;
  127. border-radius: 0.1em;
  128. border-top-color: var(--white);
  129. cursor: pointer;
  130. height: 0;
  131. padding: 0;
  132. position: relative;
  133. transform: rotate(90deg);
  134. width: 0;
  135. }
  136. .calendar-header button::after {
  137. content: &#39;&#39;;
  138. display: block;
  139. height: 25px;
  140. left: 50%;
  141. position: absolute;
  142. top: 50%;
  143. transform: translate(-50%, -50%);
  144. width: 25px;
  145. }
  146. .calendar-header button:last-of-type {
  147. transform: rotate(-90deg);
  148. }
  149. `;
  150. }
  151. render() {
  152. const monthYear = `${this.calendar.month.name}, ${this.calendar.year}`;
  153. const date = this.date.format(this.format);
  154. this.shadow.innerHTML = `
  155. &lt;style&gt;${this.styles}&lt;/style&gt;
  156. &lt;button type=&quot;button&quot; class=&quot;date-toggle&quot;&gt;${date}&lt;/button&gt;
  157. &lt;div class=&quot;calendar-dropdown ${this.visible ? &quot;visible&quot; : &quot;&quot;}
  158. ${this.position}&quot;&gt;
  159. &lt;div class=&quot;calendar-header&quot;&gt;
  160. &lt;button aria-label=&quot;previous month&quot; class=&quot;previous-month&quot; type=&quot;button&quot;&gt;&lt;/button&gt;
  161. &lt;h4 class=&quot;month-year&quot;&gt;
  162. ${monthYear}
  163. &lt;/h4&gt;
  164. &lt;button aria-label=&quot;next month&quot; class=&quot;next-month&quot; type=&quot;button&quot;&gt;&lt;/button&gt;
  165. &lt;/div&gt;
  166. &lt;/div&gt;
  167. `;
  168. }
  169. }
  170. export default DatePicker;
  171. window.customElements.get(&quot;date-picker&quot;) ||
  172. window.customElements.define(&quot;date-picker&quot;, DatePicker);

huangapple
  • 本文由 发表于 2023年2月27日 00:15:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/75573308.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定