This question made my mind really busy.
I have a set of tags like below
<a class='c1'>1</a>
<a class='c2'>2</a>
<a class='c3'>3</a>
<a class='c1'>4</a>
<a class='c1'>5</a>
<a class='c3'>6</a>
<a class='c2'>7</a>
What I need to do is to start from above and group elements together by class so that I can add a collapse to the elements. so the final results should be like this:
<div class='collapse'>
<a class='c1'>1</a>
<a class='c1'>4</a>
<a class='c1'>5</a>
</div>
<div class='collapse'>
<a class='c2'>2</a>
<a class='c2'>7</a>
</div>
<div class='collapse'>
<a class='c3'>3</a>
<a class='c3'>6</a>
</div>
I have tried JS scripts with tones of variable that is really hacky and does not work. Is there a simple and clean way to do this?
thanks.
You can do it this way
for(let i = 1; i<=3; i++){
let tags = document.querySelectorAll(`.c${i}`);
let div = document.createElement('div');
div.classList = 'collapse';
tags.forEach(tag =>{
div.appendChild(tag);
});
document.body.appendChild(div);
}
/* CSS just to demonstrate */
.collapse {
margin-block: 1rem;
border: blue solid 3px;
display:flex;
flex-direction: column;
}
<a class='c1'>1</a>
<a class='c2'>2</a>
<a class='c3'>3</a>
<a class='c1'>4</a>
<a class='c1'>5</a>
<a class='c3'>6</a>
<a class='c2'>7</a>
I'd group them before appending to get more control over each separate element
This way you can eg add an + to the first child as OP suggested
Get all the elements
Group them on className
For each part, create a div where you appendChild all the <a>'s
Append the <div>
const all = [ ...document.querySelectorAll('a') ].reduce((p, c) => {
(p[c.className] = p[c.className] || []).push(c);
return p;
}, {});
for (let groupKey in all) {
var e = document.createElement('div');
e.classList.add('collapse');
// Optionally: Add '+' to first child
all[groupKey][0].innerHTML += ' +';
all[groupKey].forEach(ee => e.appendChild(ee));
document.body.append(e)
}
.collapse { border: 1px solid red; display: flex; flex-direction: column; }
<a class='c1'>1</a>
<a class='c2'>2</a>
<a class='c3'>3</a>
<a class='c1'>4</a>
<a class='c1'>5</a>
<a class='c3'>6</a>
<a class='c2'>7</a>
Related
Goal
Avoid unnecessary event bindings.
Sample code
Comment box with a reply button for each individual comment
const btns = document.getElementsByClassName('reply-btn');
for (let i = 0; i < btns.length; i++) {
btns[i].addEventListener('click', showCommentContentAsPreview);
}
function showCommentContentAsPreview(e) {
console.log('showCommentContentAsPreview()');
// CHECK IF THIS BUTTON ALREADY BINDED !!!
const previewDiv = document.getElementById('preview');
const commentId = e.target.getAttribute('data-comment-id')
const commentDiv = document.getElementById('comment-' + commentId);
const commentText = commentDiv.querySelector('p').innerText
const closeReplyBtn = previewDiv.querySelector('button');
const previewContent = previewDiv.querySelector('.preview-content');
// set to preview
previewContent.innerText = commentText;
// show reply close button
closeReplyBtn.classList.remove('hidden');
// bind EventListener to "reply close button"
closeReplyBtn.addEventListener('click', closeReply)
function closeReply() {
console.log('bind to btn');
previewContent.innerText = '';
this.removeEventListener('click', closeReply);
closeReplyBtn.classList.add('hidden');
}
}
.hidden {
display: none;
}
.comment {
border-bottom: 1px solid #000;
padding: 5px;
}
.preview {
background-color: #ccc;
padding: 20px;
margin-top: 20px;
}
<div>
<!-- comment list -->
<div id="comment-1" class="comment">
<p>Comment Content 1</p>
<button class="reply-btn" data-comment-id="1">reply</button>
</div>
<div id="comment-2" class="comment">
<p>Comment Content 2</p>
<button class="reply-btn" data-comment-id="2">reply</button>
</div>
</div>
<!-- output -->
<div>
<div id="preview" class="preview">
<div class="preview-content"></div>
<button class="hidden">Close Preview</button>
</div>
</div>
Simulate problem
When you try the example, the following two scenarios occur:
Click reply once and then click "close preview"
Click on reply several times and then on "close preview".
Question
How can I avoid multiple bindings to the same button? I am already thinking about singleton.
Instead of binding a listener to every element in the series, you can bind a single listener once on a common parent of them all, and then use element.matches() to determine if the click target is the one that you want before doing more work. See the following example:
function logTextContent (elm) {
console.log(elm.textContent);
}
function handleClick (ev) {
if (ev.target.matches('.item')) {
logTextContent(ev.target);
}
}
document.querySelector('ul.list').addEventListener('click', handleClick);
<ul class="list">
<li class="item">Item 1</li>
<li class="item">Item 2</li>
<li class="item">Item 3</li>
<li class="item">Item 4</li>
<li class="item">Item 5</li>
</ul>
With the helpful hints from #Zephyr and #jsejcksn I have rewritten the code of the above question. Thus I have achieved my goal of avoiding multiple identical bindings to one element.
const container = document.getElementById('comment-container');
const previewDiv = document.getElementById('preview');
const closeReplyBtn = previewDiv.querySelector('button');
const previewContent = previewDiv.querySelector('.preview-content');
container.addEventListener('click', handleClick);
function handleClick(ev) {
if (ev.target.matches('.reply-btn')) {
if (ev.target.getAttribute('listener') !== 'true') {
removeOtherListenerFlags();
ev.target.setAttribute('listener', 'true');
showCommentContentAsPreview(ev);
}
}
if (ev.target.matches('#preview button')) {
previewContent.innerText = '';
closeReplyBtn.classList.add('hidden');
removeOtherListenerFlags();
}
}
function showCommentContentAsPreview(e) {
console.log('showCommentContentAsPreview()');
const commentId = e.target.getAttribute('data-comment-id')
const commentDiv = document.getElementById('comment-' + commentId);
const commentText = commentDiv.querySelector('p').innerText
// set to preview
previewContent.innerText = commentText;
// show reply close button
closeReplyBtn.classList.remove('hidden');
}
function removeOtherListenerFlags() {
const replyBtns = container.querySelectorAll('.reply-btn')
Object.keys(replyBtns).forEach((el) => {
replyBtns[el].removeAttribute('listener');
})
}
.hidden {
display: none;
}
.comment {
border-bottom: 1px solid #000;
padding: 5px;
}
.preview {
background-color: #ccc;
padding: 20px;
margin-top: 20px;
}
<div id="comment-container">
<div id="comment-listing">
<!-- comment list -->
<div id="comment-1" class="comment">
<p>Comment Content 1</p>
<button class="reply-btn" data-comment-id="1">reply 1</button>
</div>
<div id="comment-2" class="comment">
<p>Comment Content 2</p>
<button class="reply-btn" data-comment-id="2">reply 2</button>
</div>
</div>
<!-- output -->
<div>
<div id="preview" class="preview">
<div class="preview-content"></div>
<button class="hidden">Close Preview</button>
</div>
</div>
</div>
Cool and Thanks!
i fixed the old question ty for helping this is my second question
There's one more thing i need to know. How can i select a child of "user chat_user"?
var anchors = document.getElementsByClassName("user chat_user");
for(var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
anchor.onclick = function() { setTimeout(function() {
var ok = document.getElementsByClassName("user-dropdown")[0];
var za = document.createElement("li");
za.className = "user-dropdown-entry";
za.innerText = "Mention";
ok.append(za);}, 10);
}
}
You have to ensure your element is defined/exists.
Try this:
let ok = document.querySelector(".user-dropdown");
// Ensure the class `.user-dropdown` exists
if (ok) {
// Only loop through how many exist, no more
Array.from(document.getElementsByClassName("user chat_user")).forEach(anchor => {
anchor.onclick = () => {
// Use querySelector to select only the first of child here
let za = document.createElement("li");
za.className = "user-dropdown-entry";
za.innerText = "Mention";
ok.append(za);
}
});
}
.user.chat_user {
border: 1px solid black;
display: inline-block;
}
<dive class="user-dropdown">user dropdown</div>
<ul class="messages">
<li class="chat_msg msg-user-message">
<div class="colorbar user"></div>
<span class="user chat_user">
<span class="chat_user_prof">
<img src="https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/e8/e852c40b62be21a16b9f6d69a584e742d7364abb.jpg"></span>
<span class="xp_70">
<i class="icon"></i>
<span class="level_val">79</span>
</span>
<a class="chat_user_name">ChickenİX * 1</a>
<span class="chat_user_ico">
<i class="icon-gammdom-symbol"></i>
<span class="chat_user_colen">:</span>
</span>
</span>
<span class="chat_cont">İg diyon xd</span>
</li>
</ul>
No luck with all the trying and reading.
There are two types of card elements with two different groups of 15 color choices. Besides using "a" what can I do with this? Please help!!!!!!!!!!!!!!
$(document).ready(function(){
$("a").on('click mouseover', function () {
var color = $(this).attr('class');
$('#card_color_choice').removeClass().addClass("regcard cardlist "+color+"");
});
});
$(document).ready(function(){
$("a").on('click mouseover', function () {
var color = $(this).attr('class');
$('#card_color_choice2').removeClass().addClass("regcard cardlist "+color+"");
});
});
.regcard{
width: 100px;
height: 100px;
}
.orange{
background-color: #FF6600;
color: #EFEFEF;
}
.blue{
background-color: #637eb6;
color: #EFEFEF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
Color Choices:<br/>
<a href="#" id="colorchoice" class="orange" >Color1</a>
<br/><br/>
<a href="#" target="_blank">
<div id="card_color_choice" class="regcard cardlist orange" >
Element 1 inside
</div>
</a>
<br/><br/>
Different Elements:<br/>
Color3
<a href="#" target="_blank">
<div id="card_color_choice2" class="regcard cardlist orange" >
Element 2 inside
</div>
</a>
Group the trigger and the target together under the same ancestor. Then do some relative DOM traversal to find the correct target. For example:
$(document).ready(function() {
$(".trigger").on('click mouseover', function() {
var color = $(this).attr('class').replace('trigger', '');
$(this).closest('.group').find('.regcard').removeClass().addClass("regcard cardlist " + color + "");
});
});
.regcard {
width: 100px;
height: 50px;
}
.orange {
background-color: #FF6600;
color: #EFEFEF;
}
.blue {
background-color: #637eb6;
color: #EFEFEF;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="group">
Color Choices:<br/>
Color1
<a href="#" target="_blank">
<div class="regcard cardlist">
Element 1 inside
</div>
</a>
</div>
<br/>
<div class="group">
Different Elements:<br/>
Color3
<a href="#" target="_blank">
<div class="regcard cardlist">
Element 2 inside
</div>
</a>
</div>
I have 5 <a>...
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
...and I have 5 <div> and each div have the content of the <a> names. I want show all <a> and show only a <div>, so I want click on a <a> and hide the others divs showing only the selected div.
I'm searching and searching but I can´t find this exactly I found similar things but impossible to integrate to this problem.
This are the divs tags
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
#trips{ display: block };
#eats{ display: none };
#films{ display: none };
#music{ display: none };
#travels{ display: none };
This should do it, though this hides all divs on click, apart from the div matching the a tag. I'd suggest adding something to identify the divs to show/hide. This only requires vanilla js.
const test = document
.querySelectorAll('[id*="show"]')
.forEach(element => element.onclick = () => {
document
.querySelectorAll('div')
.forEach(element => {
element.style.display = 'none';
});
const divIdToShow = element.id.replace('show', '');
const divElementToShow = document.getElementById(divIdToShow);
divElementToShow.style.display = 'block';
});
console.log(test)
#trips {
display: block;
}
#eats {
display: none;
}
#films {
display: none;
}
#music {
display: none;
}
#travels {
display: none;
}
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
This is trivial. You have 2 options: either add "onclick" events on each of your tags and then listen to these events with js. Or, since you added ids to each of your tags, you can now attach click events to them with js.
So, no more words. Here's the simple solution:
document.getElementById("showtrips").onclick = toggleShowForElement(document.getElementById("trips"));
document.getElementById("showeats").onclick = toggleShowForElement(document.getElementById("eats"));
document.getElementById("showfilms").onclick = toggleShowForElement(document.getElementById("films"));
document.getElementById("showmusic").onclick = toggleShowForElement(document.getElementById("music"));
document.getElementById("showtravels").onclick = toggleShowForElement(document.getElementById("travels"));
function toggleShowForElement(element) {
return () => {
if (element.style.display === "none") {
element.style.display = "block";
} else {
element.style.display = "none";
}
}
}
body {
font-family: "Verdana", sans-serif;
}
* {
box-sizing: border-box;
}
a {
cursor: pointer;
color: #008800;
display: inline-block;
padding: 4px;
border-radius: 4px;
}
a:hover {
text-decoration: underline;
}
div {
background-color: #88DD99;
border-radius: 4px;
padding: 5px;
margin-bottom: 5px;
height: 30px;
text-align: center;
}
<a id="showtrips">TRIPS</a>
<a id="showeats">EATS</a>
<a id="showfilms">FILMS</a>
<a id="showmusic">MUSIC</a>
<a id="showtravels">TRAVELS</a>
<div id="trips">Content Trips</div>
<div id="eats">Content Eats</div>
<div id="films">Content Films</div>
<div id="music">Content Music</div>
<div id="travels">Content Travels</div>
Just added a bit of CSS to make it look a bit prettier. It's not needed here.
If you are using simple javascript you can toggle the div using the following code:
var mydiv = document.getElementById("myDIV");
if (mydiv.style.display === "none") {
mydiv.style.display = "block";
} else {
mydiv.style.display = "none";
}
If you using jQuery you can directly use toggle methods.
$( ".target" ).toggle();
I suggest you group your div and anchor tags using the class name, So it will be easy to maintain.
Your html code will be like:
<a id="showtrips" class="link">TRIPS</a>
<a id="showeats" class="link">EATS</a>
<a id="showfilms" class="link">FILMS</a>
<a id="showmusic" class="link">MUSIC</a>
<a id="showtravels" class="link">TRAVELS</a>
<div id="trips" class="content">Content Trips</div>
<div id="eats" class="content">Content Eats</div>
<div id="films" class="content">Content Films</div>
<div id="music" class="content">Content Music</div>
<div id="travels" class="content">Content Travels</div>
Your JS code will be look like:
document
.querySelectorAll('[class="link"]')
.forEach(element => element.onclick = () => {
document
.querySelectorAll('[class="content"]')
.forEach(element => {
element.style.display = 'none';
});
const mydiv = element.id.substr(4);
document.getElementById(mydiv).style.display = 'block';
});
I just created the working jsfiddle for you link here
I am trying to work out to select a delete icon in my own web application. delectIcon
HTML
<main>
<div class="container">
<div class="tabs">
<p><span class="active">Newest</span></p><a href=""><p>
<span>Oldest</span></p></a><p><span>Add</span></p>
</div>
<div class="content">
<ul>
<li>
<span class="itemLeft">Answer emails</span>
<span class="itemMiddle">12-31-2016</span>
<span class="itemRight">1</span>
<b class="deleteIcon"> X </b>
</li>
<li>
<span class="itemLeft">Prep for Monday's class</span>
<span class="itemMiddle">12-31-2016</span>
<span class="itemRight">5</span>
<b class="deleteIcon"> X </b>
</li>
</ul>
</div>
</div>
</main>
JavaScript
$(".deleteIcon").on("click", function () {
alert("Oh, clicked!");
return false;
});
I failed to do so by writing it myself. So I used Chrome Web Developer Tool to find the CSS path. I tried to use the XPath($"[/html/body/main/div/div[2]/ul/li[ 1 ]/b]") and CSS Path ($"(pathbody > main > div > div.content > ul > li:nth-child(1) > b)"). Neither of them worked.
I tried to mark it with an ID and made only one "li" exists. The CSS selector worked all right. But when I clicked the deleteIcon$"(#deleteIcon)", nothing happened.
#deleteIcon{
float:right;
font-weight: bold;
padding: 0 3px 0 3px;
border-radius: 5px;
background: #ccc;
cursor: pointer;
margin-left: 5px;
font-size: 1.3em;
text-align: center;
}
I also tried to select my title. I found the following worked out.
$(".container h1").on("click", function () {
alert("Oh, no!");
return false;
});
I do not what to do now. Can anyone help me out here?
Thank you! I would be really appreciate if you can answer my question.
Adding more details:
I did actually add the deleteIcon into the HTML by JavaScript. I do not know whether this can have an effect on my selector.
Actual HTML
<main>
<div class="container">
<div class="tabs">
<p><span class="active">Newest</span></p><a href=""><p>
<span>Oldest</span></p></a><p><span>Add</span></p>
</div>
<div class="content">
</div>
</div>
</main>
JavaScript (The important part listed below)
function Item(name,dueDate,type){
this.name=name;//1
this.dueDate=dueDate;//input2
this.type=type;//3
};
$(".tabs a span").toArray().forEach(function (element) {
var $element = $(element);
// create a click handler for this element
$element.on("click", function () {
var $content,
$input,
$button,
i;
if ($element.parent().parent().is(":nth-child(1)")) {
// newest first, so we have to go through
// the array backwards
$content = $("<ul>");
for (i = Task.length-1; i >= 1; i--) {
// $buttondelete = $("<buttonDelete>").text("X");
var txt1 = Task[i].toStringName();
var txt2 = Task[i].toStringDate();
var txt3 = Task[i].toStringType();
//alert(txt3);
$content.append('<li> <span class="itemLeft">'+txt1+'</span> <span class="itemMiddle">'+txt2+'</span> <span class="itemRight">'+txt3+'</span><b class="deleteIcon"> X </b>');
}
}
$("main .content").append($content);
return false;
});
});
If you are creating the items inside ul dynamically you should bind the click event like this :
$(".content").on("click", ".deleteIcon", function()
{
alert("clicked") ;
return false;
}
) ;
The class selector starts with a . (just like the example you say you have that works).
Try
$(".deleteIcon").on("click", function () {
alert("Oh, clicked!");
return false;
});