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

基于JS/HTML/AngularJS实现带可折叠分组的单选滚动列表/表格

Got it, let's build this collapsible grouped radio list with drag-and-drop between two lists using AngularJS, HTML, and vanilla JavaScript. I'll break this down into manageable parts so you can follow along easily.

1. Core Concept & Data Structure

First, we'll structure our data to support grouped items with collapse state. Each group will have a name, a list of items, and a flag to track if it's collapsed. We'll also track the selected radio item and manage two separate lists for drag-and-drop.

2. Collapsible Grouped List with Radio Selection

We'll use AngularJS directives like ng-repeat, ng-click, and ng-model to handle the collapse/expand and radio selection. The radio buttons will be bound to a single model to ensure only one item is selected at a time.

3. Drag-and-Drop Implementation

We'll leverage the HTML5 Drag and Drop API, wrapped in AngularJS directives to keep the logic clean. We'll handle three key events:

  • dragstart: Capture the selected item data when dragging starts
  • dragover: Allow the drop action on the target list
  • drop: Transfer the item from the source list to the target list, updating our data models

Complete Working Example

HTML

<!DOCTYPE html>
<html ng-app="collapsibleDragList">
<head>
    <style>
        .list-container {
            width: 300px;
            height: 400px;
            border: 1px solid #ccc;
            overflow-y: auto;
            float: left;
            margin: 0 20px;
            padding: 10px;
        }
        .group-header {
            cursor: pointer;
            padding: 8px;
            background-color: #f0f0f0;
            border: 1px solid #ddd;
            font-weight: bold;
        }
        .group-items {
            padding-left: 15px;
        }
        .list-item {
            padding: 6px;
            margin: 4px 0;
            border: 1px solid #eee;
            cursor: move;
        }
        .list-item.selected {
            background-color: #e3f2fd;
            border-color: #2196f3;
        }
    </style>
</head>
<body ng-controller="MainController">
    <div class="list-container">
        <h3>Source List</h3>
        <!-- Collapsible Groups -->
        <div ng-repeat="group in sourceGroups">
            <div class="group-header" ng-click="group.isCollapsed = !group.isCollapsed">
                {{group.name}} ({{group.items.length}} items)
                <span ng-show="group.isCollapsed">▼</span>
                <span ng-hide="group.isCollapsed">▲</span>
            </div>
            <div class="group-items" ng-hide="group.isCollapsed">
                <!-- Radio Items with Drag Support -->
                <div 
                    class="list-item" 
                    ng-class="{'selected': selectedItem === item}"
                    draggable="true"
                    ng-drag-start="onDragStart(item, 'source')"
                    ng-repeat="item in group.items">
                    <input type="radio" ng-model="$parent.$parent.selectedItem" ng-value="item"> {{item.name}}
                </div>
            </div>
        </div>
    </div>

    <div class="list-container">
        <h3>Target List</h3>
        <div 
            class="group-items"
            ng-drop="onDrop($event)"
            ng-dragover="allowDrop($event)">
            <div class="list-item" ng-repeat="item in targetItems">
                {{item.name}}
            </div>
            <div ng-if="targetItems.length === 0" style="color: #999; padding: 20px; text-align: center;">
                Drag items here
            </div>
        </div>
    </div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.8.2/angular.min.js"></script>
    <script src="app.js"></script>
</body>
</html>

app.js

angular.module('collapsibleDragList', [])
.controller('MainController', ['$scope', function($scope) {
    // Initialize source groups with collapse state
    $scope.sourceGroups = [
        {
            name: 'Group A',
            isCollapsed: false,
            items: [
                {id: 1, name: 'Item A1'},
                {id: 2, name: 'Item A2'},
                {id: 3, name: 'Item A3'}
            ]
        },
        {
            name: 'Group B',
            isCollapsed: true,
            items: [
                {id: 4, name: 'Item B1'},
                {id: 5, name: 'Item B2'}
            ]
        }
    ];

    // Target list for dropped items
    $scope.targetItems = [];
    $scope.selectedItem = null;

    // Drag-and-Drop Handlers
    $scope.onDragStart = function(item, source) {
        // Store item data and source type in the drag event
        event.dataTransfer.setData('text/plain', JSON.stringify({item: item, source: source}));
    };

    $scope.allowDrop = function(event) {
        event.preventDefault(); // Allow drop action
    };

    $scope.onDrop = function(event) {
        event.preventDefault();
        const data = JSON.parse(event.dataTransfer.getData('text/plain'));
        const draggedItem = data.item;

        // Remove item from source groups
        $scope.sourceGroups.forEach(group => {
            const index = group.items.findIndex(item => item.id === draggedItem.id);
            if (index !== -1) {
                group.items.splice(index, 1);
            }
        });

        // Add item to target list
        $scope.targetItems.push(draggedItem);

        // Deselect the item after drag
        $scope.selectedItem = null;
        $scope.$apply(); // Trigger AngularJS digest cycle
    };
}])
// Custom directives for drag events
.directive('ngDragStart', function() {
    return {
        link: function(scope, element, attrs) {
            element.on('dragstart', function(event) {
                scope.$apply(function() {
                    scope.$eval(attrs.ngDragStart, {$event: event});
                });
            });
        }
    };
})
.directive('ngDrop', function() {
    return {
        link: function(scope, element, attrs) {
            element.on('drop', function(event) {
                scope.$apply(function() {
                    scope.$eval(attrs.ngDrop, {$event: event});
                });
            });
        }
    };
})
.directive('ngDragover', function() {
    return {
        link: function(scope, element, attrs) {
            element.on('dragover', function(event) {
                scope.$apply(function() {
                    scope.$eval(attrs.ngDragover, {$event: event});
                });
            });
        }
    };
});

Key Features Explained

  • Collapsible Groups: Clicking the group header toggles the isCollapsed flag, showing/hiding items via ng-hide.
  • Single Radio Selection: All radio buttons bind to the same selectedItem model, ensuring only one item is selected globally.
  • Drag-and-Drop: Custom AngularJS directives wrap the HTML5 drag events to transfer item data between lists, updating the source and target data models.
  • Scrollable Lists: CSS sets a fixed height with overflow-y: auto for both containers.

Notes

  • If you need per-group radio selection instead of global, change the ng-model to group.selectedItem instead of $parent.$parent.selectedItem.
  • The example uses AngularJS 1.8.2 (the latest stable version of AngularJS).
  • Styling is kept minimal—you can customize the CSS to match your app's design.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.26 08:45:33