I have a question about targetting other element than it pointing the element clicked in the following code.
https://codepen.io/jotnajoa/pen/gOpQdzV
I don't get this part below.
const addClassToStrings = addClassToEach(strings, className);
btn.addEventListener('click', (el) => {
if (addClassToStrings.next().done)
el.target.classList.add(className);
});
If the addClassToEach event is tied into the button,
I assumed .classList.add(className) event would apply to the button itself.
Because it is targetting it.
I guess el.target..... this part does something.
Can anyone help me to understand this part?
DEMO:
const strings = document.querySelectorAll('.string');
const btn = document.querySelector('#btn');
const className = 'darker';
function* addClassToEach(elements, className) {
for (const el of Array.from(elements))
yield el.classList.add(className);
}
const addClassToStrings = addClassToEach(strings, className);
btn.addEventListener('click', (el) => {
if (addClassToStrings.next().done)
el.target.classList.add(className);
});
.string{width:50vmin;height:8vmin;margin:4vmin;transition:.3s ease;background:#90ee90}.string.darker{background:#343434;filter:blur(3vmin)}#btn{position:absolute;padding:5vmin 10vmin;color:#d6d6d6;font-size:8vmin;font-family:Roboto,sans-serif;user-select:none;background:#3749a2;border-radius:5%;top:5vmin;left:80vmin;transition:.3s ease;cursor:pointer}#btn:active,#btn:hover{border-radius:50%;box-shadow:1vmin 1vmin 1vmin grey}#btn.darker{border-radius:0;background:#343434;filter:blur(3vmin)}
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div class="string"></div>
<div id="btn">Click me</div>
Related
I want to show div content on button click .and thee is 3 different button following 3 different content. I tried this logic and it made my code lengthy. how to simplify is code using loop or condition?
function replace1(){
document.getElementById("con1").style.visibility="visible";
document.getElementById("con2").style.visibility="hidden";
document.getElementById("con3").style.visibility="hidden";
document.getElementById("con4").style.visibility="hidden";
document.getElementById("con5").style.visibility="hidden";
document.getElementById("con6").style.visibility="hidden";
}
function replace2(){
document.getElementById("con1").style.visibility="hidden";
document.getElementById("con2").style.visibility="visible";
document.getElementById("con3").style.visibility="hidden";
document.getElementById("con4").style.visibility="hidden";
document.getElementById("con5").style.visibility="hidden";
document.getElementById("con6").style.visibility="hidden";
}
function replace3(){
document.getElementById("con1").style.visibility="hidden";
document.getElementById("con2").style.visibility="hidden";
document.getElementById("con3").style.visibility="visible";
document.getElementById("con4").style.visibility="hidden";
document.getElementById("con5").style.visibility="hidden";
document.getElementById("con6").style.visibility="hidden";
}
enter image description here
.active-button {
background: red;
}
<button class="replace-button" onclick="replace(1, this)"></button>
<button class="replace-button" onclick="replace(2, this)"></button>
<button class="replace-button" onclick="replace(3, this)"></button>
function replace(visibleIndex, _this) {
const buttons = document.querySelectorAll('.replace-button');
buttons.forEach(button => button.classList.remove("active-button"));
_this.classList.add("active-button");
for(let i = 1; i < 7; i++) {
let element = document.getElementById("con" + i)
i === visibleIndex ? element.style.visibility = "visible" : element.style.visibility = "hidden";
}
}
Use a class - add class="con" to each element - also use hidden instead of visibility since the hidden divs still will take up space
const toggle = id => cons
.forEach(con => con.hidden = con.id !== id);
Here is a version that will change the colour of the button too.
You will need to use hidden or display:none to have the divs stay in one place
window.addEventListener('DOMContentLoaded', function() {
const cons = document.querySelectorAll('.con');
const buts = document.querySelectorAll('.toggle');
const toggle = id => cons
.forEach(con => con.hidden = con.id !== id);
document.getElementById('nav').addEventListener('click', function(e) {
const tgt = e.target.closest('button');
if (tgt.classList.contains('toggle')) {
toggle(tgt.dataset.id)
buts.forEach(but => but.classList.remove('active'));
tgt.classList.add('active');
}
})
})
.active {
background-color: green;
}
<nav id="nav">
<button type="button" class="toggle" data-id="con1">Con 1</button>
<button type="button" class="toggle" data-id="con2">Con 2</button>
<button type="button" class="toggle" data-id="con3">Con 3</button>
</nav>
<div id="con1" class="con" hidden>
<h1>Con 1</h1>
</div>
<div id="con2" class="con"hidden>
<h1>Con 2</h1>
</div>
<div id="con3" class="con" hidden>
<h1>Con 3</h1>
</div>
I am new to Javascript and I try to add an event listener for each button on every card, but the code make the last card (button) only have the event 'click' so is there's any way to make it happen with innerHTML card
this is the code:
let tracksRender = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button");
// added the content of the button
btn.innerHTML += inBtn;
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
}
and the structure:
<!doctype html>
<html>
<head>
<meta charset='utf-8'>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<title>SoundCloud Player</title>
<link rel='stylesheet' href='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css'>
<link rel='stylesheet' href='styles/main.css'>
<style></style>
</head>
<body id="soundcloud-player">
<div class="ui container col">
<div class="col">
<div class="main">
<div class="js-search-results search-results ui cards">
</div>
</div>
</div>
</div>
<script src="https://connect.soundcloud.com/sdk/sdk-3.3.2.js"></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.js'></script>
<script src="javascript/main.js"></script>
</body>
</html>
Fiddle: https://jsfiddle.net/y73bstju/7/
but it will add the event to the last card only
It might help to create elements instead of appending innerHTML:
let tracksRender = (track) => {
// Select the results here, so you wont have to repeat it
const searchResults = document.querySelector('.js-search-results');
track.forEach(element => {
// Create the card, give it its class and innerHTML
const card = document.createElement('div');
card.className = 'card';
card.innerHTML = `<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>`;
// Created the button, give its classes and innerHTML
const btn = document.createElement('div');
btn.className = 'ui bottom attached button js-button';
btn.innerHTML = '<i class="add icon"></i><span>Add to playlist</span>';
// Add the event listener
btn.addEventListener('click', () => {
console.log('click');
});
// Append the button to the created card
card.appendChild(btn);
// Add the card to the results
searchResults.appendChild(card);
});
}
I agree with you that theoretically the object should have the event, but the behavior we experience is that whenever another write happens at the relevant section of the DOM, the event handler is lost, which is the reason the last element has the click event. So, let's write first into the DOM and only when we are done with that should we add the event listeners, like:
let SoundCloudAPI = {};
SoundCloudAPI.init = () => {
SC.initialize({ client_id: 'cd9be64eeb32d1741c17cb39e41d254d' });
};
SoundCloudAPI.init();
SoundCloudAPI.getTrack = (inputVlue) => {
SC.get('/tracks', {
q: inputVlue
}).then((tracks) => {
console.log(tracks);
SoundCloudAPI.renderTracks(tracks);
});
}
SoundCloudAPI.getTrack("alan walker");
SoundCloudAPI.renderTracks = (track) => {
track.forEach(element => {
//this the card that will add the button for
let card = `<div class="card">
<div class="image">
<img class="image_img" src="${element.artwork_url || 'http://lorempixel.com/100/100/abstract/'}">
</div >
<div class="content">
<div class="header">
${element.title}
</div>
</div>
</div >`;
//here i add the card to DOM
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
// store the content of the button
let inBtn = `<i class="add icon"></i>
<span>Add to playlist</span>`;
// created button container
let btn = document.createElement("div");
btn.classList.add("ui", "bottom", "attached", "button", "js-button", "fresh");
// added the content of the button
btn.innerHTML += inBtn;
//here i add the button to the last card have been created
searchResults.querySelector(".card:last-child").append(btn);
});
for (let btn of document.querySelectorAll('.ui.attached.button.js-button.fresh')) {
// here i add the the event Listener to the button
btn.addEventListener('click', () => {
console.log("click");
});
}
}
Fiddle: https://jsfiddle.net/r84um9pt/
I think:
you're doing:
let searchResults = document.querySelector('.js-search-results');
searchResults.innerHTML += card;
Serializing again and again in your div "searchResults"
innerHTML ~erases~ listeners, and probably it is ~erasing~ your previous listeners. Besides, i don't remember where i read that innerHTML script code cannot run (for security purposes)
From https://medium.com/#kevinchi118/innerhtml-vs-createelement-appendchild-3da39275a694
using innerHTML reparses and recreates all DOM nodes inside the div
element and is less efficient than simply appending a new element to
the div. In the above cases, createElement is the more performant
choice.
Be careful:
Using "append" again and again uses badly browser resources, redrawing many times.
But, you can append in a documentFragment and append it to the div -js-search-results
DocumentFragment:
https://developer.mozilla.org/es/docs/Web/API/DocumentFragment
Welcome in the community. In you're code you're adding many classes to button. You can add event Listener to any one of the unique class name which is specifically applied on button element only.
You can replace:
btn.addEventListener('click', () => {
console.log('click');
});
with:
document.querySelectorAll('.js-button').forEach(el=>{
el.addEventListener('click', (event) => {
event.preventDefault();
console.log("click");
});
});
Also, it would be good if you add eventListener out of the loop in which you are appending elements.
I'm learning JavaScript and this is a practice scenario for me.
What I have already is a button that clones content, and within that content that has been cloned, there is a button to remove it.
When I click the button that prompts you to remove the content, it removes the first set of content.
What I want to happen is when you click the button that prompts you to remove the content, it removes the content related to that button and nothing else.
This is the CodePen link.
https://codepen.io/JosephChunta/pen/YzwwgvQ
Here is the code.
function addContent() {
var itm = document.getElementById("newContent");
var cln = itm.cloneNode(true);
document.getElementById("placeToStoreContent").appendChild(cln);
}
function removeContent() {
var x = document.getElementById("content").parentNode.remove();
}
// This is for debug purposes to see which content is which
document.getElementById('orderContent')
.addEventListener('click', function(e) {
const orderedNumber = document.querySelectorAll('.thisIsContent');
let i = 1;
for (p of orderedNumber) {
p.innerText = '' + (i++);
}
});
.contentThatShouldBeHidden {
display: none;
}
<div id="placeToStoreContent">
</div>
<button id="orderContent" onclick="addContent()">Add Content</button>
<div class="contentThatShouldBeHidden">
<div id="newContent">
<div id="content">
<p class="thisIsContent">This is a prompt</p>
<button onclick="removeContent()">Remove this</button>
<hr />
</div>
</div>
</div>
When you'r trying to remove by ID, it takes the first ID it finds.
To remove the correct content, send this onclick.
<button onclick="removeContent(this)">Remove this</button>
And handle it in your function:
function removeContent(el) {
el.parentNode.remove();
}
Example:
function addContent() {
var itm = document.getElementById("newContent");
var cln = itm.cloneNode(true);
document.getElementById("placeToStoreContent").appendChild(cln);
}
function removeContent(el) {
el.parentNode.remove();
}
// This is for debug purposes to see which content is which
document.getElementById('orderContent')
.addEventListener('click', function(e) {
const orderedNumber = document.querySelectorAll('.thisIsContent');
let i = 1;
for (p of orderedNumber) {
p.innerText = '' + (i++);
}
});
.contentThatShouldBeHidden { display: none; }
<div id="placeToStoreContent">
</div>
<button id="orderContent" onclick="addContent()">Add Content</button>
<div class="contentThatShouldBeHidden">
<div id="newContent">
<div id="content">
<p class="thisIsContent">This is a prompt</p>
<button onclick="removeContent(this)">Remove this</button>
<hr />
</div>
</div>
</div>
In your remove button, do this:
<!-- The "this" keyword is a reference to the button element itself -->
<button onclick="removeContent(this)">Remove this</button>
And in your javascript:
function removeContent(element) {
element.parentNode.remove();
}
I don't want the button to generate words on click I want the list to be toggle through the list of words whilst the page is loaded, like if it were a short animation clip!
here is what i got so far
JS
let greeting= () => {
let hello = ['¡Hola!', 'Ola!', 'Namaste', 'Hello', 'Bonjour','Zdravstvuyte','Nǐn hǎo','Konnichiwa','Anyoung haseyo','Goddag'];
let name = hello[Math.floor(Math.random() * hello.length)];
if( document.getElementById("name")){
document.getElementById("placeholder"). removeChild(document.getElementById("name"));
}
let element = document.createElement("div");
element.setAttribute("id", "name");
element.appendChild(document.createTextNode(name));
document.getElementById("placeholder").appendChild(element)
document.getElementById("placeholder").click(element)
}
greeting()
HTML
<section class="bg-dark text-white" onload="greeting()">
<div id="placeholder" class=" text-center h1 " style="height:fit-content"></div>
</section>
<button type ="button"class ="btn bg-dark text-white btn-block" onclick="greeting()">Press me!</button>
Thanks in advance!
To have a function run on a timer, you can use the setInterval function described here: https://www.w3schools.com/jsref/met_win_setinterval.asp
You can't attach an onload to a div. Typically onload is only used with the body tag. With that said, you can just wrap a timer and start it.
let greeting = () => {
let hello = ['¡Hola!', 'Ola!', 'Namaste', 'Hello', 'Bonjour','Zdravstvuyte','Nǐn hǎo','Konnichiwa','Anyoung haseyo','Goddag'];
let name = hello[Math.floor(Math.random() * hello.length)];
if (document.getElementById("name")){
document.getElementById("placeholder").removeChild(document.getElementById("name"));
}
let element = document.createElement("div");
element.setAttribute("id", "name");
element.appendChild(document.createTextNode(name));
document.getElementById("placeholder").appendChild(element)
document.getElementById("placeholder").click(element)
}
let runGreeting = () => {
setInterval(greeting, 2000);
};
runGreeting();
<section class="bg-dark text-white">
<div id="placeholder" class="text-center h1" style="height:fit-content">
<div id="name"></div>
</div>
</section>
I'm trying to add a Class to an img when the mouse is over an element and remove the class when the mouse is no longer over the element. I'm using object literal notation. I can't see to select the correct image, can anyone see where i'm going wrong?
let Cc = {
bindEvent: function() {
$('.title.em-below').hover( function() {
let selectedtitle = $(this);
Cc.scaleThumbnail(selectedtitle);
})
},
scaleThumbnail: function(selectedtitle) {
let $thumbnail = selectedtitle.siblings('.image-thumbnail')
let img = $thumbnail.children('img');
console.log(img);
img.addClass('thumbnail-active');
img.removeClass('thumbnail-active');
},
}
.thumbnail-active {
transform: scale(1.1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="title em-below">
Title
</div>
<a class="image-thumbnail">
<div class="wide-thumbnail em-below">
<img src="https://via.placeholder.com/350x150"/>
</div>
</a>
</div>
You need to call CC.bindEvent() to bind the event handler. And your hover function needs to toggle the class, not add it and then immediately remove it.
The img element is not a child of $thumbnail, it's the grandchild. Use .find() instead of .children().
let Cc = {
bindEvent: function() {
$('.title.em-below').hover( function() {
let selectedtitle = $(this);
Cc.scaleThumbnail(selectedtitle);
})
},
scaleThumbnail: function(selectedtitle) {
let $thumbnail = selectedtitle.siblings('.image-thumbnail')
let img = $thumbnail.find('img');
//console.log(img.attr('src'));
img.toggleClass('thumbnail-active');
},
}
Cc.bindEvent();
.thumbnail-active {
transform: scale(1.1);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="title em-below">
Title
</div>
<a class="image-thumbnail">
<div class="wide-thumbnail em-below">
<img src="https://via.placeholder.com/350x150"/>
</div>
</a>
</div>
The problem is with this line, because of which the img tag is not captured.
$thumbnail.children('img');
.children only traverses its immediate child which is .wide-thumbnail.em-below.
Use .find instead
$thumbnail.find('img');