I have a list of multiple items, a small image frame and with it all a description.
I need to bind a certain image + description to the list items, so if item5 is chosen it's showing one type of picture and description, and so on(all of them would be unique).
I am having a hard time figuring out how to do this since I'm new to js, I did try the basic, setting a class to an item, then in js fetching the class and changing the content.
Here I'm trying to change only the text, but that doesn't seem to be working out either: https://jsfiddle.net/8z37f15j/5/
HTML:
<div id="wrapper">
<div id="list">
<ol>
<li id="item1">items1</li>
<li>items2</li>
<li>items3</li>
<li>items4</li>
<li>items5</li>
<li>items6</li>
<li>items7</li>
<li>items8</li>
<li>items9</li>
<li>items10</li>
<li>items11</li>
<li>items12</li>
<li>items13</li>
<li>items14</li>
<li>items15</li>
</ol>
</div>
<div id="image-container">
<div id="image">
<img src="https://semantic-ui.com/images/wireframe/image.png" alt="">
</div>
<div id="description">
just a placeholder text for when nothing has been chosen.
</div>
</div>
</div>
CSS:
/* containers */
* {
font-family: Corbel;
}
#wrapper {
border: 1px solid red;
padding: 10px;
display: inline-flex;
}
#image,
#description,
#list {
border: 1px solid #472836;
box-sizing: border-box;
margin: 5px;
}
/* list */
#list {
width: 150px;
height: 250px;
background-color: #9AD2CB;
overflow-y: auto;
overflow-x: hidden;
}
#list ol {
list-style: none;
padding: 0;
margin: 0;
}
#list li {
padding: 5px;
}
#list li:nth-child(even) {
background-color: #91f2e6;
width: 100%;
height: 100%;
}
#list li:hover {
cursor: pointer;
color: red;
}
/* sub-container */
#image,
#description {
width: 150px;
height: 150px;
}
#image {
background-color: #D7EBBA;
}
#image img {
width: 100%;
height: 100%;
}
#description {
background-color: #FEFFBE;
overflow-y: auto;
overflow-x: hidden;
height: 95px;
}
JS:
var desc_area = document.getElementById('description');
var desc1 = "random text for desc1";
function item1(){
desc_area.innerHTML += desc1;
}
You can register each element as a key in a Map and create description and the source of an image for that element as a corresponding value (in this case object). I have registered only the first item but others can be done in the same way so right now you can click on the item1 and see the result.
const desc_area = document.getElementById('description');
const image = document.querySelector('img');
const item1 = document.querySelector('#item1');
const map = new Map();
// register item element as a key and object with corresponding description / image as value
map.set(item1, { desc: 'some description for item1', img: 'url/of/image' });
// you can bind on click handler for example
const list = document.querySelector('ol');
list.addEventListener('click', event => {
// if element that was registered in our map triggered the event
if (map.has(event.target)) {
// change text of description area
desc_area.textContent = map.get(event.target).desc;
// change src of the image
image.src = map.get(event.target).img;
}
});
/* containers */
* {
font-family: Corbel;
}
#wrapper {
border: 1px solid red;
padding: 10px;
display: inline-flex;
}
#image,
#description,
#list {
border: 1px solid #472836;
box-sizing: border-box;
margin: 5px;
}
/* list */
#list {
width: 150px;
height: 250px;
background-color: #9AD2CB;
overflow-y: auto;
overflow-x: hidden;
}
#list ol {
list-style: none;
padding: 0;
margin: 0;
}
#list li {
padding: 5px;
}
#list li:nth-child(even) {
background-color: #91f2e6;
width: 100%;
height: 100%;
}
#list li:hover {
cursor: pointer;
color: red;
}
/* sub-container */
#image,
#description {
width: 150px;
height: 150px;
}
#image {
background-color: #D7EBBA;
}
#image img {
width: 100%;
height: 100%;
}
#description {
background-color: #FEFFBE;
overflow-y: auto;
overflow-x: hidden;
height: 95px;
}
<div id="wrapper">
<div id="list">
<ol>
<li id="item1">items1</li>
<li>items2</li>
<li>items3</li>
<li>items4</li>
<li>items5</li>
<li>items6</li>
<li>items7</li>
<li>items8</li>
<li>items9</li>
<li>items10</li>
<li>items11</li>
<li>items12</li>
<li>items13</li>
<li>items14</li>
<li>items15</li>
</ol>
</div>
<div id="image-container">
<div id="image">
<img src="https://semantic-ui.com/images/wireframe/image.png" alt="">
</div>
<div id="description">
just a placeholder text for when nothing has been chosen.
</div>
</div>
</div>
I think I know what you mean and could easily work with data attributes. Just add data attribute of the image path and then grab that and update your image with the data. Something like
<li data-imgsrc="/path/to/item2/image" data-desc="Description for the image 2">items2</li>
<li data-imgsrc="/path/to/item3/image" data-desc="Description for the image3">items3</li>
<li data-imgsrc="/path/to/item4/image" data-desc="Description for the image4">items4</li>
Just make one listener for the li elements and grab the imgsrc and the desc and put them in the dom where needed.
Related
I'm trying to add height to original value in CSS.
But so far I had no luck.
html,
body {
width: 100%;
height: 100vh;
margin: 0px 0px 0px 0px;
overflow: hidden;
background-color: #FFF;
}
#Header1 {
background-color: green;
width: 100vw;
height: 10vh;
margin-bottom: 0%;
z-index: 100;
}
#Header2 {
background-color: blue;
width: 100%;
height: 4vh;
margin-bottom: 0%;
z-index: 100;
}
#Main {
height: 82vh;
width: 100%;
}
if (Header1Check == 1) {
document.getElementById("Header1").style.display = "block";
} else {
document.getElementById("Header1").style.display = "none";
document.querySelector('#Main').style.height = "initial" + "10vh";
}
if (Header2Check == 1) {
document.getElementById("Header2").style.display = "block";
} else {
document.getElementById("Header2").style.display = "none";
document.querySelector('#Main').style.height = "initial" + "4vh";
}
Basicly if the check = 0 it stops showing HEADER1/Header2, but the Main content (center) does not auto size to this.
To counteract this I just tried to add that value to the CSS but it does not appear to work.
Is there a way to achieve this?
edit:
<body>
<div id="wrapper">
<div id="Header1">
</div>
<div id="Header2">
</div>
<div id="portraitContent">
</div>
<div id="landscapeContent">
</div>
<div id="Footer">
</div>
</div>
</body>
You can use display:flex on the #wrapper element, and then set the content divs with flex:1 which means they will take up the remaining space
(see more about flexbox at: https://developer.mozilla.org/en-US/docs/Learn/CSS/CSS_layout/Flexbox)
Something like
*{box-sizing:border-box;}
html,
body {
width: 100%;
height: 100vh;
margin: 0px 0px 0px 0px;
overflow: hidden;
background-color: #FFF;
}
#wrapper {
display: flex;
flex-direction: column;
height: 100vh;
}
#wrapper > * {
border: 1px solid black;
}
#Header1 {
background-color: green;
height: 10vh;
z-index: 100;
}
#Header2 {
background-color: blue;
height: 4vh;
}
#Footer {
background-color: tomato;
height: 4vh;
}
#portraitContent,
#landscapeContent {
flex: 1;
background: teal;
}
#media (orientation: landscape) {
#portraitContent {
display: none
}
}
#media (orientation: portrait) {
#landscapeContent {
display: none
}
}
<div id="wrapper">
<div id="Header1"> header 1
</div>
<div id="Header2">header 2
</div>
<div id="portraitContent">
portrait
</div>
<div id="landscapeContent">
landscape
</div>
<div id="Footer">footer
</div>
</div>
Assuming you want to change size of an image for example you can do this simple example below
Also the property you added for height is incorrect syntax
The correct property values are from
https://www.w3schools.com/jsref/prop_style_height.asp
Property Values
Value Description
auto The browser sets the height. This is default
length Defines the height in length units
% Defines the height in % of the parent element
initial Sets this property to its default value. Read about initial
inherit Inherits this property from its parent element. Read about inherit
#HaoWu comment is correct you cannot use both properties together either or
you can assign another
//Syntax : object.style.height("")
appImg.style.height = "inital";
Refernces
prop style height w3schools
js-conventions w3schools
I have prepared a simple Example answer for your question .
const appImg = document.getElementById("appImg");
//console.log(appImg);
function minimizeImage(){
const hideImgBtn = document.getElementById("hideImgBtn");
if(!hideImgBtn){
appImg.style.display = "block";
}else {
appImg.style.height = "10vh";
};
};
.app__img{
max-width: 10vw;
max-height: 150px;
width: 100%;
height: 100%;
}
<div>
<img
src ="https://images.pexels.com/photos/12999041/pexels-photo-12999041.jpeg?auto=compress&cs=tinysrgb&w=300&lazy=load" class="app__img" id="appImg" alt="appImage"/>
</div>
<button onclick="minimizeImage()" id="hideImgBtn">
Min image size with js
</button>
html,
body {
width: 100%;
height: 100vh;
margin: 0px 0px 0px 0px;
overflow: hidden;
background-color: #FFF;
}
#wrapper {
display: flex;
flex-direction: column;
height: 100vh;
}
#wrapper > * {
border: 1px solid black;
}
#Header1 {
background-color: green;
height: 10vh;
z-index: 100;
}
#Header2 {
background-color: blue;
height: 4vh;
}
#Footer {
background-color: tomato;
height: 4vh;
}
#portraitContent,
#landscapeContent {
flex: 1;
background: teal;
}
#media (orientation: landscape) {
#portraitContent {
display: none
}
}
#media (orientation: portrait) {
#landscapeContent {
display: none
}
}
<div id="wrapper">
<div id="Header1"> header 1
</div>
<div id="Header2">header 2
</div>
<div id="portraitContent">
portrait
</div>
<div id="landscapeContent">
landscape
</div>
<div id="Footer">footer
</div>
</div>
document.querySelector('#Main').style.height = "initial" + "10vh";
"initial" + "10vh" is not valid. You can do it like this:
document.querySelector('#Main').style.height += "10vh";
Good luck!
I am trying to set up a JS/jQuery/CSS solution to select images larger than 400px and set them to be the full width of the container (min-width: 100%).
However, it should not apply to images smaller than 400px to avoid selecting really small ones or thumbnails. Normally, I'd just use classes, but the markup is generated by an old wiki intranet system which doesn't give users the ability to set classes.
Any help would be appreciated.
.container {
width: 700px;
border: solid 1px red;
}
.container img {
border: solid 1px green;
max-width: 100%;
height: auto;
display: block;
margin: auto auto;
}
<div class="container">
<img src="https://via.placeholder.com/140x100" />
<img src="https://via.placeholder.com/500x100?text=Should_be_full_width" />
</div>
You can use the naturalWidth property after the images are loaded (use the window load event) and manually add a class (use classList).
window.addEventListener('load', () => {
const images = document.querySelectorAll('img');
for (let image of images) {
if (image.naturalWidth >= 400) {
image.classList.add('full-width');
// or set the style directly if you have to
// image.style.minWidth = '100%';
}
}
});
.container {
width: 700px;
border: solid 1px red;
}
.container img {
border: solid 1px green;
max-width: 100%;
height: auto;
display: block;
margin: auto auto;
}
.full-width {
min-width: 100%;
}
<div class="container">
<img src="https://via.placeholder.com/140x100" />
<img src="https://via.placeholder.com/500x100?text=Should_be_full_width" />
</div>
$(function() {
$('img').each((i, img) => {
let width = parseInt($(img).css('width'));
if (width > 400) {
$(img).css('width', '100%');
}
});
});
.container {
width: 700px;
border: solid 1px red;
}
.container img {
border: solid 1px green;
max-width: 100%;
height: auto;
display: block;
margin: auto auto;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<img src="https://via.placeholder.com/140x100" />
<img src="https://via.placeholder.com/500x100?text=Should_be_full_width" />
</div>
This would work
var images = $("img")
for (let i = 0; i < images.length; i++) {
if (images[i].width >= 400) {
images[i].style.minWidth = "100%";
}
}
I have two toggles (toggle-1 and toggle-2) with different contents in a header. I would like to prevent the user to have both toggles active simultaneously (otherwise they overlap).
In the code below I tried to use if statements to hide one of the toggles if the other is already opened but it does not work.
Ideally, what I would like to happen is that if toggle-1 is active and the user clicks on toggle-2, then toggle-1 would come back to its original state and toggle-2 would be now active. The same the other way around.
I am not familiar with JavaScript yet and I'd really appreciate if you could tell me what I have done wrong and how it should be done to have my ideal result
Here's the link to my CodePen if you find it easier:
https://codepen.io/fergos2/pen/NWWxgEp
var myToggle
var oneToggle = $(document).ready(function() {
$('.toggle-1').click(function() {
$('.toggle-1').toggleClass('active')
$('.toggle-1-content').toggleClass('active')
})
})
var twoToggle = $(document).ready(function() {
$('.toggle-2').click(function() {
$('.toggle-2').toggleClass('active')
$('.toggle-2-content').toggleClass('active')
})
})
if (myToggle == oneToggle) {
$(document).ready(function() {
$('toggle-2-content').hide();
})
} else if (myToggle == twoToggle) {
$('toggle-1-content').hide();
}
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle-1,
.toggle-2 {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.toggle-1.active,
.toggle-2.active {
background-color: red;
}
.toggle-1-content,
.toggle-2-content {
display: none;
}
.toggle-1-content.active,
.toggle-2-content.active {
display: block;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
.toggle-1-content.active {
left: 0;
}
.toggle-2-content.active {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle-1">1</div>
<div class="toggle-1-content">
<p>Some content 1</p>
</div>
<div class="toggle-2">2</div>
<div class="toggle-2-content">
<p>Some content 2</p>
</div>
</div>
</div>
Several issues.
Please study the code below
too many $(document.ready... and no need to store the result of such a statement
Using a data-attribute and a common class, shortens the code a lot. DRY Don't repeat yourself
I simplified the content containers CSS too
$(function() { // on page load
$('.toggle').on("click", function() { // any of the toggles
const $wrapper = $(this).closest(".wrapper");
const id = $(this).data("id");
$(this).toggleClass('active'); // toggle clicked div
const show = $(this).is(".active"); // is it active after we toggled?
$wrapper
.find(".toggle") // find all toggles
.not(this) // exclude the one we clicked
.removeClass("active"); // remove class
$wrapper.find(".content").hide(); // hide any content divs
$("#" + id).toggle(show); // show the one belonging to the clicked toggle
})
})
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.active {
background-color: red;
}
.content {
display: none;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
#div1 {
left: 0;
}
#div2 {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle" data-id="div1">1</div>
<div id="div1" class="content">
<p>Some content 1</p>
</div>
<div class="toggle" data-id="div2">2</div>
<div id="div2" class="content">
<p>Some content 2</p>
</div>
</div>
</div>
Working code:
$(document).ready(function() {
$('.toggle-1').click(function() {
if ($('.toggle-2').hasClass('active')) {
// remove toggle-2 active classes
$('.toggle-2').removeClass('active');
$('.toggle-2-content').removeClass('active');
}
$('.toggle-1').toggleClass('active');
$('.toggle-1-content').toggleClass('active');
});
$('.toggle-2').click(function() {
if ($('.toggle-1').hasClass('active')) {
// remove toggle-1 active classes
$('.toggle-1').removeClass('active');
$('.toggle-1-content').removeClass('active');
}
$('.toggle-2').toggleClass('active');
$('.toggle-2-content').toggleClass('active');
});
});
Here is the link to my working version.
A few things to keep in mind:
You don't need to call $(document).ready() multiple times. There's just no reason to call it multiple times on a single page as the event is only fired once.
You need to keep track of state somehow; hence the if ($('el').hasClass('classname')) syntax. Once you handle that properly, it's easy to ensure that each element is 'reset' to its original state when the other is clicked.
Hope that helps!
toggleClass accepts a second boolean parameter that forces the type of toggle, on or off. More than that you can also target multiple elements with a single jQuery call, so use that to your advantage since the classes applied have the same name.
So you could simplify your code to
$(document).ready(function() {
$('.toggle-1').click(function() {
$('.toggle-1, .toggle-1-content').toggleClass('active');
$('.toggle-2, .toggle-2-content').toggleClass('active', false)
})
$('.toggle-2').click(function() {
$('.toggle-2, .toggle-2-content').toggleClass('active');
$('.toggle-1, .toggle-1-content').toggleClass('active', false)
})
})
.container {
width: 100%;
height: 1000px;
margin: 0 auto;
background-color: #eee;
}
.wrapper {
background-color: pink;
position: relative;
display: flex;
align-items: center;
}
.toggle-1,
.toggle-2 {
display: block;
width: 20px;
height: 20px;
float: left;
cursor: pointer;
color: white;
text-align: center;
background-color: green;
margin: 10px;
}
.toggle-1.active,
.toggle-2.active {
background-color: red;
}
.toggle-1-content,
.toggle-2-content {
display: none;
}
.toggle-1-content.active,
.toggle-2-content.active {
display: block;
background-color: white;
border: 1px solid black;
position: absolute;
top: 40px;
}
.toggle-1-content.active {
left: 0;
}
.toggle-2-content.active {
left: 50px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="container">
<div class="wrapper">
<div class="toggle-1">1</div>
<div class="toggle-1-content">
<p>Some content 1</p>
</div>
<div class="toggle-2">2</div>
<div class="toggle-2-content">
<p>Some content 2</p>
</div>
</div>
</div>
You can use the method "removeClass" to remove the active class from the other toggle
var oneToggle = $(document).ready(function() {
$(".toggle-1").click(function() {
$(".toggle-1").toggleClass("active")
$(".toggle-1-content").toggleClass("active")
$(".toggle-2").removeClass("active")
$(".toggle-2-content").removeClass("active")
})
})
var twoToggle = $(document).ready(function() {
$(".toggle-2").click(function() {
$(".toggle-1").removeClass("active")
$(".toggle-1-content").removeClass("active")
$(".toggle-2").toggleClass("active")
$(".toggle-2-content").toggleClass("active")
})
})
let slider = document.getElementById("slider");
let rightBtn = document.getElementById("rightbutton");
let leftBtn = document.getElementById("leftbutton");
let element = document.getElementById("elementtype").innerHTML;
let celciusBoiling = document.getElementById("celciusboiling").value;
let chlorine = ["Chlorine", 100, 200];
function moveSliderRight() {
if (rightBtn.onclick) {
slider.value++;
}
}
function moveSliderLeft() {
if (leftBtn.onclick) {
slider.value--;
}
}
function main() {
moveSliderRight();
moveSliderLeft();
if (slider.value == parseInt(2)) {
element = chlorine[0];
celciusBoiling = chlorine[1];
}
}
main();
* {
margin: 0;
padding: 0;
box-sizing: border-box;
background-color: peachpuff;
}
header {
width: 90%;
margin: 10px auto 0px;
}
header h1 {
text-align: center;
border: 1px solid black;
padding: 15px 0px;
}
.navbar {
width: 75%;
margin: 50px auto 50px;
padding: 10px 0px;
display: flex;
justify-content: space-around;
border: 1px solid black;
}
.navlinks {
border-right: 1px solid black;
width: 50%;
text-align: center;
display: block;
}
#nav3 {
border: none;
}
#intro {
margin: 0px auto 50px;
width: 40%;
text-align: center;
}
#slider {
-webkit-appearance: none;
background-color: grey;
width: 90%;
display: block;
margin: auto;
}
#slider::-webkit-slider-thumb {
cursor: pointer;
}
#slider::-moz-range-thumb {
cursor: pointer;
}
#valuetag {
text-align: center;
margin-top:25px;
}
h2 {
text-align: center;
font-size: 45px;
text-decoration: underline;
}
#display {
width: 90%;
margin-left: 50px;
margin-bottom: 50px;
font-size: 40px;
}
#display div {
display: inline-block;
width: 45%;
text-align: center;
}
span {
font-size: 15px;
}
.boiling {
margin-left: 6%;
}
.boilingpointslider {
text-align: center;
}
button {
margin: 20px 20px 20px 0px;
width: 75px;
}
<header>
<h1>Periodic Table Gases - Interative Slider</h1>
<nav>
<div class="navbar">
<div class="navlinks">Boiling Point</div>
<div class="navlinks" id="nav3">Melting Point</div>
</div>
</nav>
</header>
<div id="intro">
<p>Interact with the slider buttons to view the displayed properties held by gases, within the periodic table of elements.</p>
</div>
<h2 id="elementtype">Hydrogen</h2>
<div id="display">
<div class="boiling">
<h2>Boiling Point</h2>
<input id="celciusboiling" type="number" value="0"><span>℃</span>
<input id="fahrenboiling" type="number"><span>℉</span>
<input id="kelvinboiling" type="number"><span>K</span>
</div>
<div class="melting">
<h2>Melting Point</h2>
<input id="celciusmelting" type="number"><span>℃</span>
<input id="fahrenmelting" type="number"><span>℉</span>
<input id="kelvinmelting" type="number"><span>K</span>
</div>
</div>
<input type="range" min="0" max="9" value="0" id="slider">
<div class="boilingpointslider">
<button id="leftbutton" onclick="moveSliderLeft()">Left</button>
<button id="rightbutton" onclick="moveSliderRight()">Right</button>
</div>
I am having issues transferring a value to an input field.
Within the snippet linked their is a heading with the value hydrogen and to the bottom left their is a boiling point heading with a input field for celcius.
I'm trying to achieve a scenario whereby you move the slider along using the buttons and at each value the heading changes to a different element and the input value for just the celcius boiling point changes.
I can't get this to work though. The buttons are working to make the slider move left and right, but for whatever reason i cant get the value to appear within the input field or change the heading. I've displayed the code i have already to get the buttons to move the slider and a snippet of what i thought would allow the changes i want to take place when the slider value changes to 2. I cant get it to to work though
Thanks.
You don't show your HTML, but I presume that slider is an input (text or hidden).
The value attribute is a string, even if you assign it a number, so you need to first convert it to a integer if you want to increment or decrement it, like so:
slider.value = parseInt(slider.value)++ // or --
Note that also you are trying to parseInt(2) down in your main(), which makes no sense as 2 is already an integer.
I have some nested elements on my page with a same handler on them which should be called only for an event target without affecting elements higher in DOM tree. To achieve this behavior I used stopPropagation method and it was ok. Then I had to add some handlers for body and other elements outside the nested divs which should be called in any case. Of course stopPropagation isn't an option now but how can I make it work?
Here is a sample:
html:
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="no-handler"></div>
</div>
</div>
</div>
</div>
css:
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#no-handler {
width: 150px;
height: 150px;
padding: 30px;
border: solid 1px #888;
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
js:
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function nestedHandler(e) {
e.stopPropagation();
this.classList.add("nested-clicked");
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
jsfiddle link:
http://jsfiddle.net/6kgnu7fr/
clicking on .nested should add red background color to clicked element and add green color to outer body and #container
UPD:
http://jsfiddle.net/6kgnu7fr/2/
clicking on #no-event or any other element inside .nested should also call nestedHandler for this .nested element.
You can check for the event's target in your nestedHandler instead of stopping the propagation. Change the class only if the target is this so that the effet will only be applied for the div on which the event occurred:
function nestedHandler(e) {
if (e.target === this) {
this.classList.add("nested-clicked");
}
}
Edit
Following your edit, this is harder. Way to do it is to find e.target's first ancestor with the "nested" class, then doing the comparison with it instead of target:
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
This is naïve shot. You may want to look for a way to make it more efficient, e.g. by avoiding to look for the first ancestor on each .nested div.
See the working snipped below.
var container = document.getElementById("container");
var nested = document.getElementsByClassName("nested");
function outerHandler(e) {
this.classList.add("outer-clicked");
}
function findAncestorWithClass(dom, targetClass){
if(!dom){
return; // (undefined)
}
if(dom.classList.contains(targetClass)){
return dom;
}
// terminal recursion
return findAncestorWithClass(dom.parentNode, targetClass);
}
function nestedHandler(e) {
var nestedParent = findAncestorWithClass(e.target, "nested");
if (this === nestedParent) {
nestedParent.classList.add("nested-clicked");
}
}
container.addEventListener("click", outerHandler, false);
document.body.addEventListener("click", outerHandler, false);
for (var i = 0; i < nested.length; i++) {
nested[i].addEventListener("click", nestedHandler, false);
}
#container {
display: block;
width: 398px;
height: 398px;
padding: 30px;
border: solid 1px #888;
}
#nested1 {
width: 336px;
height: 336px;
padding: 30px;
}
#nested2 {
width: 274px;
height: 274px;
padding: 30px;
}
#nested3 {
width: 212px;
height: 212px;
padding: 30px;
}
#sub-nested {
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.4);
}
.nested {
border: solid 1px #888;
}
.nested-clicked {
background-color: red;
}
.outer-clicked {
background-color: green;
}
<div id="container">
<div id="nested1" class="nested">
<div id="nested2" class="nested">
<div id="nested3" class="nested">
<div id="sub-nested"></div>
</div>
</div>
</div>
</div>