含分号/冒号的data-target导致aria-expanded按钮无法展开问题
解决Bootstrap折叠组件中ID含特殊字符无法触发展开的问题
核心问题分析
- aria-controls属性错误:你当前代码里给
aria-controls加了#前缀,这个属性不需要#,它是直接关联目标元素的ID,不是CSS选择器。 - data-target选择器未转义:Bootstrap的
data-target使用CSS选择器语法,而:、;属于CSS特殊字符,直接使用会导致选择器解析失败,无法匹配目标元素。
解决方案
1. 修正aria-controls属性
直接去掉aria-controls里的#,只保留目标ID:
aria-controls="vsn-{{vs_feat_name[1:]}}-end"
2. 给data-target的选择器转义特殊字符
在Jinja2中添加自定义过滤器来转义CSS特殊字符,这样既能保留原始字符的业务含义,又能让选择器生效:
# 在Flask应用中注册自定义过滤器 @app.template_filter('escape_css') def escape_css_selector(value): # 转义CSS选择器中的特殊字符,HTML属性中需要双反斜杠 return value.replace(':', '\\:').replace(';', '\\;')
然后在模板中使用这个过滤器处理data-target:
data-target="#vsn-{{vs_feat_name[1:]|escape_css}}-end"
3. 保留原始ID
目标折叠容器的ID不需要修改,直接保留包含特殊字符的原始值即可(HTML规范允许ID包含:、;这类字符):
<div class="collapse" id="vsn-{{vs_feat_name[1:]}}-end"> <!-- 折叠内容 --> </div>
4. 手动初始化折叠(可选,解决极端兼容问题)
如果上述调整后仍有问题,可以手动用JS初始化折叠逻辑,绕过Bootstrap内置选择器的解析问题:
// 等待DOM加载完成 document.addEventListener('DOMContentLoaded', function() { document.querySelectorAll('[data-toggle="collapse"]').forEach(btn => { // 获取目标ID(从aria-controls直接取,避免选择器解析问题) const targetId = btn.getAttribute('aria-controls'); const target = document.getElementById(targetId); if (!target) return; // 绑定点击事件 btn.addEventListener('click', function() { // 切换折叠状态 target.classList.toggle('show'); // 更新aria-expanded属性 btn.setAttribute('aria-expanded', target.classList.contains('show')); }); }); });
修正后的完整代码示例
<button tabindex='0' role="button" type="button" class="btn btn-outline-dark m-1" data-toggle="collapse" data-target="#vsn-{{vs_feat_name[1:]|escape_css}}-end" aria-expanded="false" aria-controls="vsn-{{vs_feat_name[1:]}}-end"> <span style="font-weight:bold; text-transform:uppercase;">{{ vs_feat_name+' ('+vs_val['count']|string+')' }}</span> </button> <div class="collapse" id="vsn-{{vs_feat_name[1:]}}-end"> <div class="card border border-dark" style="max-height: 500px; overflow-y: scroll; padding: 1rem"> <div class="card-header"> <p> Frames containing <span style="text-transform: uppercase;">{{vs_feat_name}}: </span>{{ vs_val['vn_class_mem']|length }}<br> </p> </div> <div class="card card-body"> <h5> {% for vn_mem in vs_val['vn_class_mem'] %} <button tabindex='0' role="button" type="button" class="btn btn-outline-dark m-1" data-toggle="button" onclick="window.location.href='{{ url_for( 'render_vn_class', vn_class_id=vn_mem) }}';"> <span style="font-weight:bold; text-transform:uppercase;">{{ vn_mem }}</span> </button> {% endfor %} </h5> </div> </div> </div>
内容的提问来源于stack exchange,提问作者Akhilesh
相关产品推荐
相关产品推荐

