Angular JS手风琴组件异常:时而可展开时而无法展开
Hey there! Let's troubleshoot that inconsistent accordion issue you're facing in AngularJS—sometimes it expands, sometimes it doesn't. Looking at your code snippet, I can point out a few key issues and fixes to get it working reliably.
1. The Most Likely Culprit: Global Variable Pollution
In your dropdown method, you have $this = $(this) without using var, let, or const. This makes $this a global variable, which can get overwritten by other click events or multiple accordion instances. When this happens, the accordion loses track of which element was clicked, leading to unpredictable behavior.
Quick Fix: Add variable scoping:
Accordion.prototype.dropdown = function(e) { var $el = e.data.el; var $this = $(this); // Add var/let/const here var $next = $this.next(); $next.slideToggle(); $this.parent().toggleClass('open'); if (!e.data.multiple) { // Finish the truncated logic to close other submenus $el.find('.submenu').not($next).slideUp().parent().removeClass('open'); } }
2. AngularJS + jQuery Event Binding Conflict
You're using angular.element(document).ready() and jQuery's links.on('click'), but AngularJS has its own DOM lifecycle. If your accordion elements are dynamically rendered (e.g., with ng-repeat), jQuery's static event binding won't attach to new elements. Even for static elements, binding events outside Angular's context can lead to timing issues where the DOM isn't fully ready when your code runs.
Better AngularJS-native Solution (Recommended)
Instead of mixing jQuery with Angular, build a reusable accordion directive that works with Angular's lifecycle:
Step 1: Create the Directives
angular.module('yourApp').directive('accordion', function() { return { restrict: 'E', scope: { multiple: '@' // Allow multiple open sections if true }, transclude: true, template: '<div class="accordion-container" ng-transclude></div>', controller: function($scope) { let openSections = []; this.toggleSection = function(section) { const sectionIndex = openSections.indexOf(section); if (sectionIndex > -1) { // Collapse the section openSections.splice(sectionIndex, 1); } else { // Expand the section—close others if multiple is false if (!$scope.multiple) openSections = []; openSections.push(section); } }; this.isSectionOpen = function(section) { return openSections.includes(section); }; } }; }); angular.module('yourApp').directive('accordionSection', function() { return { require: '^accordion', restrict: 'E', scope: { title: '@' }, transclude: true, template: ` <div class="accordion-section"> <div class="section-link" ng-click="toggle()"> {{title}} <span class="chevron" ng-class="{'rotated': isOpen()}">▼</span> </div> <div class="section-content" ng-show="isOpen()" ng-transclude></div> </div> `, link: function(scope, element, attrs, accordionCtrl) { scope.toggle = () => accordionCtrl.toggleSection(scope); scope.isOpen = () => accordionCtrl.isSectionOpen(scope); } }; });
Step 2: Use the Directives in HTML
<accordion multiple="false"> <accordion-section title="First Section"> Lorem ipsum dolor sit amet, consectetur adipiscing elit. </accordion-section> <accordion-section title="Second Section"> Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. </accordion-section> </accordion>
Step 3: Add Basic CSS
.accordion-section { border: 1px solid #eee; margin-bottom: 8px; } .section-link { padding: 12px; cursor: pointer; background-color: #f8f8f8; display: flex; justify-content: space-between; align-items: center; } .section-content { padding: 12px; } .chevron { transition: transform 0.2s ease; } .chevron.rotated { transform: rotate(180deg); }
3. Final Checks
- Ensure no other JavaScript is blocking the click event (e.g.,
event.stopPropagation()in another listener) - If you stick with your original jQuery approach, use event delegation for dynamic elements:
this.el.on('click', '.link', {el: this.el, multiple: this.multiple}, this.dropdown)
内容的提问来源于stack exchange,提问作者Techguy

