英文:
Using only JS and css, can I implement a filter, so that: clicking/tapping within a JS dropdown menu shows/hides page divs by data-type?
问题
通过SO会员the.marolie的帮助,我学会了如何实现JS过滤器/选择器[称为“演示JS过滤器”]。我已经在测试页面上完美地实现了这个功能:根据在HTML主体中为它们分配的“数据类型”,它可以显示/隐藏<div>
元素。然而,选择是通过滑动下拉列表<select>
并在首选选项上释放来进行的。这不是我想要的。
我想要利用现有的导航栏下拉菜单[“我的下拉菜单”]作为过滤器/选择器。我特别想保留我的下拉菜单的现有交互性,其中单击/轻触会显示整个下拉内容块,单击内容块外部会关闭它。
我希望我的下拉菜单中的元素表示HTML页面的各种显示/隐藏<div>
选项,并允许用户通过额外的单击/轻触选择它们(基本上是演示JS过滤器所做的事情,但由单击/轻触发起)。一旦通过导航栏的单击/轻触显示,整个下拉内容块必须保持在屏幕上,就像当前所做的那样,这才有可能。
在将我的下拉标识为id="media-selector-demo"和name="filter"后,我希望能够将演示JS过滤器的<option>
元素分配给其中的<a>
元素,然后整个事情会像演示JS过滤器的<select>
下拉列表一样运作。我有一个模糊的想法,使用<a>
元素可能不需要JS中的另一个onClick
。我已经尝试了各种<a>
和<option>
元素的组合,但尚未取得任何进展。
我是否需要另一个onClick
来通过我的下拉菜单调用JS过滤器?还是我可以通过活动的<a>
状态调用JS过滤器?
我正在通过反复试验来解决这个问题。
以下是我认为与上述所有内容相关的代码部分:
我的下拉菜单基于以下代码。
页面头部的JS:
/* 当用户单击按钮时,切换隐藏和显示下拉内容 */
function myDropdownJS() {
document.getElementsByClassName("navbarDROPDOWN-JS")[0].classList.toggle("show");
}
// 如果用户单击内容之外的地方,关闭下拉菜单
window.onclick = function(e) {
if (!e.target.matches('.dropbtn')) {
var myDropdown = document.getElementsByClassName("navbarDROPDOWN-JS")[0];
if (myDropdown.classList.contains('show')) {
myDropdown.classList.remove('show');
}
}
}
我的下拉菜单HTML在导航栏中(大部分CSS只是设计样式):
<span class="dropdown" onclick="myFunction()">
<a class="dropbtn navbarDROP-ICON" style="opacity:0.5; padding-top:0px;">menu</a>
<a class="dropbtn navbarDROP-TXT" style="opacity:0.5">menu </a>
<a class="dropbtn navbarDROP-TXT">Career Works by Date </a>
<div class="dropdown-content navbarDROPDOWN-JS" >
<a class="tag-bgd-INSTLLN" href="#">Installations (all media)</a>
<a class="tag-bgd-MOVIMG" href="#">Works with moving image (inc. vid/film releases)</a>
<a class="tag-bgd-SNDMUS" href="#">...with sound and music (inc. sound/music releases)</a>
<a class="tag-bgd-PHOTO" href="#">...with photographs</a>
<a class="tag-bgd-DRAW" href="#">...with drawing (inc. 2D works)</a>
<a class="tag-bgd-TXT" href="#">...with text</a>
<a class="tag-bgd-PERF" href="#">...with performative action</a>
<a class="tag-bgd-COLPUB" href="#">Collaborative and public works</a>
<a class="tag-bgd-OBJDEV" href="Objects, garments, devices</a>
<a class="tag-bgd-EDPUB" href="Editions, publications</a>
<a class="tag-bgd-CAREER" href="#">Career Works by Date</a>
</div>
上述<a href>
元素将包含不同样式页面的URL。如果我可以通过此下拉菜单使用户有选择地显示/隐藏页面的不同部分,那么就不需要这些URL。
演示JS过滤器基于以下代码(通过SO用户the.marolie提供)。
页面末尾的JS:
var select = document.getElementById('media-selector-demo');
var filter;
select.addEventListener("change", function() {
filter = select.value;
var elements = document.querySelectorAll('.wk-date_ITEM');
elements.forEach((el) => {
var type = el.dataset.type.split(', ');
if (type.includes(filter)) {
el.classList.remove('hide-by-media');
} else {
el.classList.add('hide-by-media');
}
})
});
演示JS过滤器的CSS:
.hide-by-media {
display: none;
}
演示JS过滤器的页面主体中的HTML:
<select id="media-selector-demo" name="filter">
<option value="INSTLLN"> Installations (all media)</option>
<option value="MOVIMG"> Works with moving image (inc. vid/film releases)</option>
<option value="SNDMUS"> ...with sound and music (inc. sound/music releases)</option>
</select>
示例页面主体中的div(共有80-100个):
<!-- ++++++++++ START FULL-WIDTH LIST ENTRY '2017 STATE OF DREAD' ++++++++++ -->
<div id="state-of-dread" class="w3-container wk-date_ITEM" data-type="INSTLLN, SNDMUS">
<div class="w3-container wk-date_TXT-IMG">
<div class="wk-date_GRID
<details>
<summary>英文:</summary>
With the help of SO member the.marolie I learned how to implement a JS filter/selector [the 'demo JS filter']. I have this working perfectly on a test page: it shows/hides divs according to "data-types" assigned to them in the body html. However, the selection is made by sliding down a `<select>` dropdown list and letting go at the preferred option. That's not quite what I want.
I want to utilise my existing nav-bar dropdown ['my dropdown'] as the filter/selector. I especially want to retain the existing interactivity of my dropdown, whereby one click/tap reveals the whole of the dropdown content block, and one click outside the content block closes it.
I want the elements within my dropdown to represent various show/hide `<div>` 'options' for the html page, and enable the user to choose from these via an additional click/tap (essentially what the demo JS filter does, but at the instigation of a click/tap). Once revealed via a nav-bar click/tap, the whole dropdown content block has to stay on-screen -as it currently does- for this to be practically possible.
After making my dropdown identifiable via id="media-selector-demo" and name="filter" I was hoping that I could assign the demo JS filter's `<option>` elements to the `<a>` elements in it, and the whole thing would function like the `<select>` dropdown of the demo JS filter. I had a vague idea that using `<a>` elements might obviate the need for another `onClick` in the JS. I've tried various combinations of `<a>` and `<option>` elements, but nothing has worked yet.
Do I need another `onClick` to invoke the JS filter via my dropdown? or
Can I invoke the JS filter via 'active' `<a>` status?
I'm struggling by trial and error.
Here are what I think are the relevant sections of code pertaining to all discussed above:
**My dropdown** is based on the following code.
**JS** in the page head:
/* When the user clicks on the button,
toggle between hiding and showing the dropdown content */
function myDropdownJS() {
document.getElementsByClassName("navbarDROPDOWN-JS")[0].classList.toggle("show");
}
// Close the dropdown if the user clicks outside of it
window.onclick = function(e) {
if (!e.target.matches('.dropbtn')) {
var myDropdown = document.getElementsByClassName("navbarDROPDOWN-JS")[0];
if (myDropdown.classList.contains('show')) {
myDropdown.classList.remove('show');
}
}
}
**My dropdown** **html** in nav bar (most of the css is just design styling):
<span class="dropdown" onclick="myFunction()">
<a class="dropbtn navbarDROP-ICON" style="opacity:0.5; padding-top:0px;">menu</a>
<a class="dropbtn navbarDROP-TXT" style="opacity:0.5">menu </a>
<a class="dropbtn navbarDROP-TXT">Career Works by Date </a>
<div class="dropdown-content navbarDROPDOWN-JS" >
<a class="tag-bgd-INSTLLN" href="#">Installations (all media)</a>
<a class="tag-bgd-MOVIMG" href="#">Works with moving image (inc. vid/film releases)</a>
<a class="tag-bgd-SNDMUS" href="#">...with sound and music (inc. sound/music releases)</a>
<a class="tag-bgd-PHOTO" href="#">...with photographs</a>
<a class="tag-bgd-DRAW" href="#">...with drawing (inc. 2D works)</a>
<a class="tag-bgd-TXT" href="#">...with text</a>
<a class="tag-bgd-PERF" href="#">...with performative action</a>
<a class="tag-bgd-COLPUB" href="#">Collaborative and public works</a>
<a class="tag-bgd-OBJDEV" href="#">>Objects, garments, devices</a>
<a class="tag-bgd-EDPUB" href="#">Editions, publications</a>
<a class="tag-bgd-CAREER" href="#">Career Works by Date</a>
</div>
Above: the `<a href>` elements were going to contain URLs for alternatively styled pages. There is no need for these if I can enable the user to selectively show/hide parts of just this one page, via this dropdown.
The **demo JS filter** is based on the following code (via SO user the.marolie).
**JS** at page end:
var select = document.getElementById('media-selector-demo');
var filter;
select.addEventListener("change", function() {
filter = select.value;
var elements = document.querySelectorAll('.wk-date_ITEM');
elements.forEach((el) => {
var type = el.dataset.type.split(', ');
if (type.includes(filter)) {
el.classList.remove('hide-by-media');
} else {
el.classList.add('hide-by-media');
}
})
});
**Demo JS filter** **CSS**:
.hide-by-media {
display: none;
}
**Demo JS filter** **html** in page body:
<select id="media-selector-demo" name="filter">
<option value="INSTLLN"> Installations (all media)</option>
<option value="MOVIMG"> Works with moving image (inc. vid/film releases)</option>
<option value="SNDMUS" >...with sound and music (inc. sound/music releases)</option>
</select>
Example **div in page body** (there are 80-100 of these):
<!-- ++++++++++ START FULL-WIDTH LIST ENTRY '2017 STATE OF DREAD' ++++++++++ -->
<div id="state-of-dread" class="w3-container wk-date_ITEM" data-type="INSTLLN, SNDMUS">
<div class="w3-container wk-date_TXT-IMG">
<div class="wk-date_GRID">
<div class= "h3 wk-date_DATE"> 2017 </div>
<div class="wk-date_TTL"><h1>State of Dread</h1></div>
<div class="h2 wk-date_KIND-1" >Installation</div>
<div class="p wk-date_KIND-2" ><span class="sound">Sound</span>, for x2 interconnected rooms.<br>AB, CD, EF, Solo exhibition (as trio), Ohrenhoch sound gallery, Berlin.</div>
<div class="wk-date_IMG">
<div class="w3-container w3-right wk-date_IMG-BOX-LSCP">
<img src="../../imgs/INSTALLATION-EVENT/2017_dread_thmb.jpg"
alt="'xx' by Andrew Stones, installation view, xx"></div>
</div>
</div>
</div>
</div>
<!-- ++++++++++ END FULL-WIDTH LIST ENTRY '2017 STATE OF DREAD' ++++++++++ -->
**Demo JS filter**: **JS** at end of page:
<script type="text/javascript">
var select = document.getElementById('media-selector-demo');
var filter;
select.addEventListener("change", function() {
filter = select.value;
var elements = document.querySelectorAll('.wk-date_ITEM');
elements.forEach((el) => {
var type = el.dataset.type.split(', ');
if (type.includes(filter)) {
el.classList.remove('hide-by-media');
} else {
el.classList.add('hide-by-media');
}
})
});
</script>
</details>
# 答案1
**得分**: 0
你需要做的是将事件侦听器从`select, change`更改为`下拉元素, 点击`,还需要将选择框中选项的值作为`data-value`属性添加到下拉元素上。
1 - 为表示要隐藏的元素添加一个`data-value`属性
<a class="tag-bgd-INSTLLN" href="#" data-value="INSTLLN">
2 - 目标是要附加事件侦听器的下拉元素。
const dropDownElements = document.querySelectorAll('.dropdown-content a')
3 - 附加事件侦听器到所选目标(PS. 函数中的`e`代表事件,点击事件侦听器生成一个事件对象)
dropDownElements.forEach((dropDownElement) => {
dropDownElement.addEventListener('click',(e)=>{
const filter = e.target.dataset.value;
})
})
4 - 其余部分只是添加在**演示js过滤器**中使用的其余过滤器
dropDownElements.forEach((dropDownElement) => {
dropDownElement.addEventListener("click", (e) => {
const filter = e.target.dataset.value
var elements = document.querySelectorAll(".wk-date_ITEM")
elements.forEach((el) => {
var type = el.dataset.type.split(", ")
if (type.includes(filter)) {
el.classList.remove("hide-by-media")
} else {
el.classList.add("hide-by-media")
}
})
})
})
<details>
<summary>英文:</summary>
What you would need to do is change the event listener from `select, change` to `drop down element, click`. you would also need to add the values of the options from the select as data-value attributes on the drop down elements.
1 - add a data-value attribute to the elements to represent what to hide
<a class="tag-bgd-INSTLLN" href="#" data-value="INSTLLN">
2 - target the drop down elements you want to attach the event listener to.
const dropDownElements = document.querySelectorAll('.dropdown-content a')
3 - attach event listeners to the selected targets (PS. the e in the function stands for event, click event listener produces an event object)
dropDownElements.forEach((dropDownElement) => {
dropDownElement.addEventListener('click',(e)=>{
const filter = e.target.dataset.value;
})
})
4 - the rest is just adding the rest of the filter used in the **demo js filter**
dropDownElements.forEach((dropDownElement) => {
dropDownElement.addEventListener("click", (e) => {
const filter = e.target.dataset.value
var elements = document.querySelectorAll(".wk-date_ITEM")
elements.forEach((el) => {
var type = el.dataset.type.split(", ")
if (type.includes(filter)) {
el.classList.remove("hide-by-media")
} else {
el.classList.add("hide-by-media")
}
})
})
})
</details>
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论