You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular应用中CSS与JS实现的Collapsible组件无法展开求助

Collapsible Component Works in Plain HTML but Fails in Angular

Problem Description

I've built a collapsible component that works perfectly in a plain HTML page, but when migrating it to an Angular app, the panels won't expand at all. Here's what I did during the migration:

  • Copied the HTML markup into my component's .html file
  • Moved all CSS styles to the component's .css file
  • Pasted the vanilla JavaScript code into the app's index.html file

I've double-checked all IDs and markup structure, and everything matches the working plain HTML version. I can't figure out why the toggle functionality isn't triggering in Angular.

Here's the original working plain HTML code:

<!DOCTYPE html>
<html>
<head>
    <title></title>
    <style>
        button.accordion {
            background-color: #777;
            color: white;
            cursor: pointer;
            padding: 18px;
            width: 100%;
            border: none;
            text-align: left;
            outline: none;
            font-size: 15px;
        }

        button.accordion.active, button.accordion:hover {
            background-color: #555;
        }

        button.accordion:after {
            content: '\002B';
            color: white;
            font-weight: bold;
            float: right;
            margin-left: 5px;
        }

        button.accordion.active:after {
            content: '';
            background-image: url('download.jpg');
            display: inline-block;
            background-size: 30px 30px;
            width:30px;
            height:30px;
            transform:rotate(180deg);
        }

        div.panel {
            padding: 0 18px;
            background-color: #f1f1f1;
            max-height: 0;
            overflow: hidden;
            transition: max-height 0.2s ease-out;
        }

        * {
            box-sizing: border-box;
        }

        #myUL {
            list-style-type: none;
            padding: 0;
            margin: 0;
        }

        a{
            text-decoration: none;
            color:white;
        }
    </style>
</head>
<body>
    <h2>Collapsible</h2>
    <p>Click the button to toggle between showing and hiding the collapsible content.</p>
    <div id="div2">
        <button class="accordion"><a href="#">Addq</a></button>
        <div class="panel">
            <p>Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. Lorem ipsum dolor sit amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
            <button class="accordion"><a href="#">Aollapsible</a></button>
            <div class="panel">
                <p>sdfsdfsdfsdt amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
            </div>
            <button class="accordion"><a href="#" style="">Dollapsible</a></button>
            <div class="panel">
                <p>qqqqqqqqqqqpsible content. consequat.</p>
            </div>
            <button class="accordion"><a href="#">Qollapsible</a></button>
            <div class="panel">
                <p>zzzzzzzzzllapsible content. commodo consequat.</p>
            </div>
        </div>
    </div>
    <script>
        var acc = document.getElementsByClassName("accordion");
        var i;
        for (i = 0; i < acc.length; i++) {
            acc[i].onclick = function() {
                this.classList.toggle("active");
                var panel = this.nextElementSibling;
                if (panel.style.maxHeight){
                    panel.style.maxHeight = null;
                } else {
                    panel.style.maxHeight = panel.scrollHeight + "px";
                }
            }
        }
    </script>
</body>
</html>

Solution

The core issue is that your vanilla JavaScript in index.html runs before Angular renders your component's view, so it can't find any .accordion elements to attach click listeners to. Plus, Angular has its own lifecycle and best practices that avoid direct DOM manipulation from outside the component. Here's how to fix it properly:

1. Move the logic to your component's TypeScript file

Instead of using global JS, handle the click events within your Angular component after its view has initialized:

import { Component, AfterViewInit, ElementRef, ViewChildren, QueryList } from '@angular/core';

@Component({
  selector: 'app-collapsible',
  templateUrl: './collapsible.component.html',
  styleUrls: ['./collapsible.component.css']
})
export class CollapsibleComponent implements AfterViewInit {
  // Query all accordion buttons using a template reference
  @ViewChildren('accordionBtn') accordionButtons!: QueryList<ElementRef>;

  ngAfterViewInit(): void {
    // Attach click listeners once the view is fully loaded
    this.accordionButtons.forEach(btnRef => {
      const button = btnRef.nativeElement;
      button.addEventListener('click', () => {
        button.classList.toggle('active');
        const panel = button.nextElementSibling;
        
        if (panel.style.maxHeight) {
          panel.style.maxHeight = null;
        } else {
          panel.style.maxHeight = panel.scrollHeight + 'px';
        }
      });
    });
  }
}

2. Add template references to your component's HTML

Add a #accordionBtn template variable to each accordion button so Angular can find them:

<h2>Collapsible</h2>
<p>Click the button to toggle between showing and hiding the collapsible content.</p>
<div id="div2">
    <button #accordionBtn class="accordion"><a href="#">Addq</a></button>
    <div class="panel">
        <p>Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. Lorem ipsum dolor sit amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
        <button #accordionBtn class="accordion"><a href="#">Aollapsible</a></button>
        <div class="panel">
            <p>sdfsdfsdfsdt amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
        </div>
        <button #accordionBtn class="accordion"><a href="#" style="">Dollapsible</a></button>
        <div class="panel">
            <p>qqqqqqqqqqqpsible content. consequat.</p>
        </div>
        <button #accordionBtn class="accordion"><a href="#">Qollapsible</a></button>
        <div class="panel">
            <p>zzzzzzzzzllapsible content. commodo consequat.</p>
        </div>
    </div>
</div>

3. Verify CSS encapsulation

Since you moved CSS to the component's .css file, Angular's default view encapsulation should apply styles correctly. If you notice styles not working, you can either:

  • Use ::ng-deep to pierce encapsulation (note: this is deprecated but still works for legacy use cases):
    ::ng-deep button.accordion {
      /* styles here */
    }
    
  • Or disable view encapsulation for the component:
    @Component({
      // ... other settings
      encapsulation: ViewEncapsulation.None
    })
    

Alternative: Angular-native approach (no direct DOM manipulation)

For a cleaner, more Angular-friendly solution, use property binding instead of manipulating DOM styles directly:

HTML:

<h2>Collapsible</h2>
<p>Click the button to toggle between showing and hiding the collapsible content.</p>
<div id="div2">
    <button class="accordion" (click)="togglePanel(panel1)" [class.active]="panel1Open">
      <a href="#">Addq</a>
    </button>
    <div #panel1 class="panel" [style.maxHeight]="panel1Open ? panel1.scrollHeight + 'px' : '0'">
        <p>Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. Lorem ipsum dolor sit amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
        
        <button class="accordion" (click)="togglePanel(panel2)" [class.active]="panel2Open">
          <a href="#">Aollapsible</a>
        </button>
        <div #panel2 class="panel" [style.maxHeight]="panel2Open ? panel2.scrollHeight + 'px' : '0'">
            <p>sdfsdfsdfsdt amet,ome Some collapsible content. Click the button to toggle between showing and hiding the collapsible content. </p>
        </div>
        
        <!-- Repeat for other panels -->
    </div>
</div>

TypeScript:

import { Component, ElementRef } from '@angular/core';

@Component({
  selector: 'app-collapsible',
  templateUrl: './collapsible.component.html',
  styleUrls: ['./collapsible.component.css']
})
export class CollapsibleComponent {
  panel1Open = false;
  panel2Open = false;
  // Add boolean variables for other panels

  togglePanel(panel: ElementRef): void {
    // Toggle the corresponding panel's open state
    if (panel.nativeElement.id === 'panel1') {
      this.panel1Open = !this.panel1Open;
    } else if (panel.nativeElement.id === 'panel2') {
      this.panel2Open = !this.panel2Open;
    }
    // Add conditions for other panels
  }
}

This approach follows Angular's best practices by avoiding direct DOM manipulation and using data binding instead.

内容的提问来源于stack exchange,提问作者Maddy8381

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 08:51:20