I have a set of img them work as tabs and over them img of locks.
my goal is to have the lock witch is the img on top be made hidden and the tab img become clickable.
HTML
<div id="Tabs">
<img class="tablinks" id="tab_button_1" onclick="tabEvent('tab_button_1', event, 'tab1')" src="Assets/Button_Tabs_Center.png" style="width:80px;height:28px;">
<img class="tablinks" id="tab_button_2" onclick="tabEvent('tab_button_2', event, 'tab2')" src="Assets/Button_Tabs_Center.png" style="width:80px;height:28px;">
<img class="tablinks" id="tab_button_3" onclick="tabEvent('tab_button_3', event, 'tab3')" src="Assets/Button_Tabs_Center.png" style="width:80px;height:28px;">
</div>
<div id="Tabs_locks">
<img id="tab_lock_1" src="Assets/lock.png" style="position:relative;width:18px;height:28px;left:30px">
<img id="tab_lock_2" src="Assets/lock.png" style="position:relative;width:18px;height:28px;left:30px">
<img id="tab_lock_3" src="Assets/lock.png" style="position:relative;width:18px;height:28px;left:30px">
</div>
Javascript
function tabEvent(id, evt, tabName) {
var tabcontent, tablinks;
tabcontent = document.getElementsByClassName("tabcontent");
for (i = 0; i < tabcontent.length; i++) {
tabcontent[i].style.display = "none";
}
tablinks = document.getElementsByClassName("tablinks");
for (i = 0; i < tablinks.length; i++) {
tablinks[i].className = tablinks[i].className.replace(" active", "");
}
document.getElementById(tabName).style.display = "block";
evt.currentTarget.className += " active";
}
document.getElementById("tab_lock_1").style.visibility = "hidden"
document.getElementById("tab_lock_2").style.visibility = "hidden"
document.getElementById("tab_lock_3").style.visibility = "hidden"
there is some css code that moves the imgs on top of each other with the tabs being z index 1 and the locks being z index 2.
whenever I make the lock img hidden it disappears but the tab img under it is not clickable it is clickable if the lock img is not in the code.
Key here is to add the CSS that makes the lock layer "click through" with pointer-events: none;, here let me demonstrate.
I have my functions in a namespace to make it "contained" in my "tabthing" and cache some things at the start in my tabthings object but other than that it is pretty standard.
I also dispensed with all the ID's and in-line styles and used classes as more standard when working with visual elements.
(function(tabthing, undefined) {
var tabthings = {
tabClassName: "tab-link",
tablockClassName: "tab-lock",
tabcontentClassName: "tab-content",
activeClassList: "active-show",
inactiveClassList: "inactive-hide",
tabLinks: {},
tabLocks: {},
tabContents: {},
linksParent: {},
locksParent: {},
contentParent: {}
};
function toggleclass(thisnode, isTrue = true) {
thisnode.classList.toggle(tabthings.activeClassList, isTrue);
thisnode.classList.toggle(tabthings.inactiveClassList, !isTrue);
}
function toggleActive(thisnode) {
toggleclass(thisnode, true);
}
function toggleInActive(thisnode) {
toggleclass(thisnode, false)
}
function toggleRelated(thisnode) {
let meIndex = [].indexOf.call(tabthings.linksParent.children, thisnode);
thisnode.classList.toggle("active", true);
if (thisnode.classList.contains("active")) {
toggleActive(tabthings.tabContents[meIndex]);
toggleInActive(tabthings.tabLocks[meIndex]);
} else {
inActivateAll(tabthings.tabLocks);
}
}
function inActivateAll(things) {
var i = things.length;
while (i--) {
toggleInActive(things[i]);
}
}
function activateAll(things) {
for (let i = 0; i < things.length; i++) {
toggleActive(things[i]);
}
}
function tabClickHandler(event) {
let meTab = this; //event.target;//same as this
// already active, do nothing
if (this.classList.contains('active')) {
inActivateAll(tabthings.tabContents);
toggleActive(tabthings.tabContents[0]);
meTab.classList.toggle("active", false);
return;
}
activateAll(tabthings.tabLocks);
inActivateAll(tabthings.tabContents);
for (let i = 0; i < tabthings.tabLinks.length; i++) {
tabthings.tabLinks[i].classList.toggle("active", false);
}
toggleRelated(meTab);
}
tabthing.setup = function() {
tabthings.linksParent = document.getElementById("tab-link-container");
tabthings.locksParent = document.getElementById("tab-lock-container");
tabthings.contentParent = document.getElementById("tab-content-container");
tabthings.tabLinks = document.getElementsByClassName(tabthings.tabClassName);
tabthings.tabLocks = document.getElementsByClassName(tabthings.tablockClassName);
tabthings.tabContents = document.getElementsByClassName(tabthings.tabcontentClassName);
// add event handlers
for (let i = 0; i < tabthings.tabLinks.length; i++) {
tabthings.tabLinks[i].addEventListener("click", tabClickHandler);
}
// trigger first tab click event
const clickevent = new Event('click');
tabthings.tabLinks[0].dispatchEvent(clickevent);
};
}(window.tabthing = window.tabthing || {}));
// call the setup function
tabthing.setup();
.mass-container {
border: 1px solid lime;
display: flex;
}
#tab-link-container,
#tab-lock-container {
grid-column: 1;
grid-row: 1;
}
#tab-link-container {
width: 100%;
display: grid;
grid-template-columns: repeat(3, 1fr);
grid-gap: 1rem;
border: 1px solid red;
top: 1px;
grid-row-start: 1;
grid-row-end: 12;
}
#tab-lock-container {
width: 100%;
margin-left: -100%;
display: grid;
border: 1px solid blue;
top: 1px;
grid-row-start: 1;
grid-row-end: 1;
grid-column-start: 1;
grid-column-end: 12;
pointer-events: none;
}
#tab-content-container {
border: solid cyan 1px;
display: grid;
}
.tab-content {
grid-row-start: 1;
grid-row-end: 1;
grid-column-start: 1;
grid-column-end: 12;
}
.tab-link,
.tab-lock {
margin-left: 1rem;
margin-right: 1rem;
margin-top: 1rem;
margin-bottom: 1rem;
}
.tab-lock {
display: flex;
justify-content: flex-end;
}
.tab-lock-1 {
grid-column-start: 1;
grid-column-end: 4;
}
.tab-lock-2 {
grid-column-start: 5;
grid-column-end: 8;
}
.tab-lock-3 {
grid-column-start: 9;
grid-column-end: 12;
}
.active-show {
visibility: visible;
}
.inactive-hide {
visibility: hidden;
}
.active {
background-color: #dddddd;
}
<div class="mass-container">
<div id="tab-link-container">
<span class="tab-link tab-link-1"><img src="Assets/Button_Tabs_Center.png" alt="first-img" /></span>
<span class="tab-link tab-link-2"><img src="Assets/Button_Tabs_Center.png" alt="second-img" /></span>
<span class="tab-link tab-link-3"><img src="Assets/Button_Tabs_Center.png" alt="third-img" /></span>
</div>
<div id="tab-lock-container">
<span class="tab-lock tab-lock-1 "><img src="Assets/lock.png" alt="1lck" /></span>
<span class="tab-lock tab-lock-2"><img src="Assets/lock.png" alt="2lck" /></span>
<span class="tab-lock tab-lock-3"><img src="Assets/lock.png" alt="3lck" /></span>
</div>
</div>
<div id="tab-content-container">
<div class="tab-content"><h2>h2 is good</h2>First off this is content</div>
<div class="tab-content"><h2>tab2 cool</h2>Second we have more content</div>
<div class="tab-content"><h2>tab3 Yay</h2>Now, this is still content</div>
</div>
The tab img is not clickable because although you have hidden the lock img, it still exists on top of the tab img. It doesn't disappear. Its like keeping one layer on the other.
What you can do is, use hover property. You can change the image on hover, instead of keeping 2 images one on the other.
or you could keep the lock image clickable.
Using hover property will be a better coding practice.
Edit : Another way is using a div with the onclick event and changing the background for div on hover
<html>
<head>
<title>Change img on hover</title>
</head>
<style>
#imgDiv1{
width : 100px ;
height : 100px ;
display : block;
background: url("Assets/lock.png");
}
#imgDiv1:hover {
width : 100px;
height : 100px ;
display : block;
background: url("Assets/Button_Tabs_Center.png") ;
}
</style>
<body>
<div id="imgDiv1" onclick="tabEvent()"></div>
<!-- Add more divs as required -->
</body>
</html>
Related
I am trying to create a custom HTML component which creates tabs for every child element that it has. Here is the code for that custom component:
const code_window = document.getElementById("window");
class CodeWindow extends HTMLElement {
constructor() {
super();
this.attachShadow({
mode: 'open'
});
this.shadowRoot.appendChild(code_window.content.cloneNode(true));
this.code = this.shadowRoot.getElementById("code");
this.tabbar = this.shadowRoot.getElementById("tabbar");
this.code.addEventListener("slotchange", e => {
var elems = e.target.assignedNodes();
for (let i = 0; i < elems.length; i++) {
var tab = document.createElement("div");
tab.classList.add("tab");
var name = elems[i].getAttribute("name");
tab.innerHTML = name;
tab.setAttribute("name", name);
this.tabbar.appendChild(tab);
if (elems[i].hasAttribute("active")) this.activateTab(name);
tab.addEventListener("click", e => this.activateTab(e.target.getAttribute("name")));
}
if (!this.previous_tab && this.tabbar.children.length > 0) this.activateTab(this.tabbar.children[0].getAttribute("name"));
});
}
activateTab(name) {
if (this.previous_tab) {
this.previous_frame.removeAttribute("active");
this.previous_tab.removeAttribute("active");
}
var tabs = this.tabbar.children,
frames = this.code.assignedElements();
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].getAttribute("name") == name) {
tabs[i].setAttribute("active", "");
frames[i].setAttribute("active", "");
this.previous_frame = frames[i];
this.previous_tab = tabs[i];
break;
}
}
}
}
customElements.define("c-window", CodeWindow);
<template id="window">
<style>
:host {
display: flex;
flex-direction: column;
}
#titlebar {
display: flex;
background-color: green;
}
#tabbar {
flex-grow: 1;
display: flex;
}
#tabbar .tab {
display: flex;
align-items: center;
padding: 5px 10px;
background-color: #bbb;
}
#tabbar .tab[active] {
background-color: #fff;
}
#code {
flex-grow: 1;
display: block;
position: relative;
}
#code::slotted(*) {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
padding: 5px;
display: none;
}
#code::slotted(*[active]) {
display: block;
}
</style>
<div id="titlebar">
<div id="tabbar"></div>
</div>
<slot id="code" name="frames"></slot>
</template>
<c-window style="border: 2px solid green; height: 300px;">
<div slot="frames" name="Content 1">Contents of tab 1</div>
<div slot="frames" name="Content 2">Contents of tab 2</div>
</c-window>
There is a slot inside the custom component and I want that every time a child element is added, a tab should be added, if a child element is removed, the tab associated with that child should be removed.
But the slotchange event carries no information about which element has been added/removed, so currently, I have to loop through the slot.assignedElements() every time slotchange event is called and create tabs for each and every element. It means that for a particular child, duplicate tabs will be created, which can also be quite CPU intensive.
So, I was thinking if there is some way to get the information about the modified element so that the action can be performed on only the modified element. Is there any way of getting only the modified element? If not, what method can I apply for achieving this?
You are doing the oldskool Switch-DIVs approach for Tabs
You can do it all with one shadowDOM <slot ="active">
and one click handler
Needs some more work; but you get the slot="active" concept
<template id="TABS-MEISTER">
<style>
#bar { display:flex }
#bar div { width: 100px; background: lightgreen ; margin-right:1em ; cursor:pointer}
</style>
<div id="bar"></div>
<div style="clear:both"><slot name="active"></slot></div>
</template>
<tabs-meister>
<div title="Tab1">Tab #1</div>
<div slot="active" title="Tab2">Tab #2</div>
<div title="Tab3">Tab #3</div>
</tabs-meister>
<script>
customElements.define('tabs-meister', class extends HTMLElement {
constructor() {
let template = (id) => document.getElementById(id).content.cloneNode(true);
super()
.attachShadow({mode: 'open'})
.append(template(this.nodeName));
this.onclick = (evt) => {
if (this.activetab) this.activetab.removeAttribute("slot");
let tab = evt.composedPath()[0];
this.activetab = this.querySelector(`[title="${tab.title}"]`);
this.activetab.slot = 'active';
}
}
connectedCallback() {
let tabs = [...this.children].map(node => {
return `<div title=${node.title}>${node.title}</div>`;
}).join ``;
this.shadowRoot.querySelector("#bar").innerHTML = tabs;
}
});
</script>
Sorry for the possible duplication, but I did not find a solution to my problem.
About my problem. I am trying to create a slide carousel using only js and css.
What I need is an alternation of slides when we press the “forward” or “back” buttons (for example, the previous slide goes off to the left, while the new slide simultaneously appears from the right when “forward” is pressing).
The code is here:
var slideIndex = 0;
var prev_slideIndex = slideIndex;
function myanimate() {
str = 'slideIndex=' + slideIndex + ' prev_slideIndex=' + prev_slideIndex;
var slides = document.getElementsByClassName("child");
for (var i = 0; i < slides.length; i++) slides[i].style.display = "none";
if (prev_slideIndex < slideIndex) {
if (slideIndex > 3) slideIndex = 0;
slides[prev_slideIndex].style.left = '-100%';
slides[prev_slideIndex].style.marginleft = '0%';
slides[slideIndex].style.left = '0%';
slides[slideIndex].style.marginleft = '0%';
slides[prev_slideIndex].style.animation = slides[slideIndex].style.animation = 'caroussel 1.5s';
} else {
if (slideIndex < 0) slideIndex = 3;
slides[prev_slideIndex].style.left = '100%';
slides[prev_slideIndex].style.marginleft = '0%';
slides[slideIndex].style.left = '0%';
slides[slideIndex].style.marginleft = '0%';
slides[prev_slideIndex].style.animation = slides[slideIndex].style.animation = 'caroussel_back 1.5s';
}
slides[prev_slideIndex].style.display = 'block';
slides[slideIndex].style.display = 'block';
prev_slideIndex = slideIndex;
str += ' final prev_slideIndex=' + prev_slideIndex;
document.getElementById("text").innerHTML = str;
}
.parent {
background-color: red;
display: block;
position: relative;
width: 600px;
height: 100px;
padding-top: 10px;
padding-bottom: 10px;
overflow: hidden;
}
.child {
background-color: green;
display: block;
position: absolute;
width: 100%;
height: 100px;
color: #FFFFFF;
font-size: 24px;
text-align: center;
}
#keyframes caroussel {
from {
margin-left: 100%
}
to {
margin-left: 0%
}
}
#keyframes caroussel_back {
0% {
margin-left: -100%
}
100% {
margin-left: 0%
}
}
<input type="submit" value='forward' title="sdg" onclick="slideIndex++;myanimate();">
<input type="submit" value='backward' title="sdg" onclick="slideIndex--;myanimate();">
<br>
<div class="parent">
<div class="child" style="background-color:yellow;left:0%;">
Caption1<br>Caption1 Caption1 Caption1 Caption Caption Caption Caption Caption Caption
</div>
<div class="child" style="left:100%;">
Caption2<br>Caption2 Caption2 Caption2 Caption2 Caption Caption Caption Caption Caption
</div>
<div class="child" style="background-color:magenta;left:100%;">
Caption3<br>Caption3 Caption3 Caption3 Caption3 Caption Caption Caption Caption Caption
</div>
<div class="child" style="background-color:cyan;left:100%;">
Caption4<br>Caption4 Caption4 Caption4 Caption4 Caption Caption Caption Caption Caption
</div>
</div>
<br>
<span id="text"></span>
It works correctly if we press the “forward” and “back” buttons alternately (we can observe two slides), but it works incorrectly if we press one of these buttons several times (the previous slide disappears).
Does anyone know why it doesn’t work correctly or maybe there are any ideas how to improve the code?
Thank you in advance.
I tested in Firefox 74.0 (32bit). 'transform: translateX(...)' in css gives me the same behavior.
The slide with the prev_slideIndex already got the same value for the animation on the previous iteration of the carousel. Therefore you need to use reflow to force this slide to play the same animation one more time:
slides[prev_slideIndex].offsetHeight; /* trigger reflow */
var slideIndex = 0;
var prev_slideIndex = slideIndex;
function myanimate() {
str = 'slideIndex=' + slideIndex + ' prev_slideIndex=' + prev_slideIndex;
var slides = document.getElementsByClassName("child");
for (var i = 0; i < slides.length; i++) slides[i].style.display = "none";
if (prev_slideIndex < slideIndex) {
if (slideIndex > 3) slideIndex = 0;
slides[prev_slideIndex].style.left = '-100%';
slides[prev_slideIndex].style.marginleft = '0%';
slides[slideIndex].style.left = '0%';
slides[slideIndex].style.marginleft = '0%';
slides[prev_slideIndex].offsetHeight; /* trigger reflow */
slides[prev_slideIndex].style.animation = slides[slideIndex].style.animation = 'caroussel 1.5s';
} else {
if (slideIndex < 0) slideIndex = 3;
slides[prev_slideIndex].style.left = '100%';
slides[prev_slideIndex].style.marginleft = '0%';
slides[slideIndex].style.left = '0%';
slides[slideIndex].style.marginleft = '0%';
slides[prev_slideIndex].offsetHeight; /* trigger reflow */
slides[prev_slideIndex].style.animation = slides[slideIndex].style.animation = 'caroussel_back 1.5s';
}
slides[prev_slideIndex].style.display = 'block';
slides[slideIndex].style.display = 'block';
prev_slideIndex = slideIndex;
str += ' final prev_slideIndex=' + prev_slideIndex;
document.getElementById("text").innerHTML = str;
}
.parent {
background-color: red;
display: block;
position: relative;
width: 600px;
height: 100px;
padding-top: 10px;
padding-bottom: 10px;
overflow: hidden;
}
.child {
background-color: green;
display: block;
position: absolute;
width: 100%;
height: 100px;
color: #FFFFFF;
font-size: 24px;
text-align: center;
}
#keyframes caroussel {
from {
margin-left: 100%
}
to {
margin-left: 0%
}
}
#keyframes caroussel_back {
0% {
margin-left: -100%
}
100% {
margin-left: 0%
}
}
<input type="submit" value='forward' title="sdg" onclick="slideIndex++;myanimate();">
<input type="submit" value='backward' title="sdg" onclick="slideIndex--;myanimate();">
<br>
<div class="parent">
<div class="child" style="background-color:yellow;left:0%;">
Caption1<br>Caption1 Caption1 Caption1 Caption Caption Caption Caption Caption Caption
</div>
<div class="child" style="left:100%;">
Caption2<br>Caption2 Caption2 Caption2 Caption2 Caption Caption Caption Caption Caption
</div>
<div class="child" style="background-color:magenta;left:100%;">
Caption3<br>Caption3 Caption3 Caption3 Caption3 Caption Caption Caption Caption Caption
</div>
<div class="child" style="background-color:cyan;left:100%;">
Caption4<br>Caption4 Caption4 Caption4 Caption4 Caption Caption Caption Caption Caption
</div>
</div>
<br>
<span id="text"></span>
I have a CSS grid layout and want to get the cell position below my cursor.
Imagine the following example:
If my cursor is where the star in the image is, I’m looking for a javascript function which returns {grid-row-start: 2, grid-row-end: 3, grid-column-start: 5, grid-column-end: 6}.
Does anybody of you knows about a native function to do this? Or do i have to calculate the values myself?
Thanks :)
The code of my example looks like the following:
<html>
<head>
<style>
.container {
display: grid;
grid-gap: 10px;
background-color: blue;
grid-template-columns: repeat(6, 1fr);
height: 400px;
}
.container div {
background-color: green;
}
.grid-stack-item {
}
</style>
</head>
<body>
<div class="container">
<div class="grid-stack-item" data-gs-x="0" data-gs-y="0" data-gs-width="2" data-gs-height="1">
<div class="grid-stack-item-content">1</div>
</div>
<div class="grid-stack-item" data-gs-x="2" data-gs-y="0" data-gs-width="2" data-gs-height="2">
<div class="grid-stack-item-content">2</div>
</div>
<div class="grid-stack-item" data-gs-x="4" data-gs-y="0" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content" style="overflow: hidden">3</div>
</div>
<div class="grid-stack-item" data-gs-x="5" data-gs-y="0" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content"> 4</div>
</div>
<div class="grid-stack-item" data-gs-x="0" data-gs-y="1" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content">5</div>
</div>
<div class="grid-stack-item" data-gs-x="1" data-gs-y="1" data-gs-width="1" data-gs-height="2">
<div class="grid-stack-item-content">6</div>
</div>
<div class="grid-stack-item" data-gs-x="0" data-gs-y="2" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content">8</div>
</div>
<div class="grid-stack-item" data-gs-x="2" data-gs-y="2" data-gs-width="2" data-gs-height="1">
<div class="grid-stack-item-content">9</div>
</div>
<div class="grid-stack-item" data-gs-x="4" data-gs-y="2" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content">10</div>
</div>
<div class="grid-stack-item" data-gs-x="5" data-gs-y="2" data-gs-width="1" data-gs-height="1">
<div class="grid-stack-item-content">11</div>
</div>
</div>
<script>
for(let elm of document.getElementsByClassName('grid-stack-item')){
elm.style['grid-column-start'] = parseInt(elm.getAttribute('data-gs-x')) + 1;
elm.style['grid-column-end'] = parseInt(elm.getAttribute('data-gs-x')) + parseInt(elm.getAttribute('data-gs-width')) + 1;
elm.style['grid-row-start'] = parseInt(elm.getAttribute('data-gs-y')) +1 ;
elm.style['grid-row-end'] = parseInt(elm.getAttribute('data-gs-y')) + parseInt(elm.getAttribute('data-gs-height')) +1;
}
</script>
</body>
</html>
What you want to do is figure out which element the mouse is over, and then get the window.getComputedStyle(element).gridColumnStart (and others) from that element.
The simplest way to do this would be to just add a mouseenter event to each of the grid-stack-item elements.
Here's a rough example:
let currentCellPosition = {
"grid-row-start": null,
"grid-row-end": null,
"grid-column-start": null,
"grid-column-end": null
}
Array.from(document.getElementsByClassName("grid-stack-item")).forEach(element => {
element.addEventListener("mouseenter", () => {
const style = window.getComputedStyle(element);
currentCellPosition = {
"grid-row-start": style.gridRowStart,
"grid-row-end": style.gridRowEnd,
"grid-column-start": style.gridColumnStart,
"grid-column-end": style.gridColumnEnd
}
})
})
In this example, currentCellPosition will always have the most recent cell position.
document.addEventListener("DOMContentLoaded", () => {
const output = document.getElementById("output");
Array.from(document.getElementsByClassName("cell")).forEach(element => {
element.addEventListener("mouseenter", () => {
const style = window.getComputedStyle(element)
output.innerHTML = `
row start: ${style.gridRowStart}
row end: ${style.gridRowEnd}
column start: ${style.gridColumnStart}
column end: ${style.gridColumnEnd}
`;
})
})
});
.container {
border: 2px solid green;
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
height: 200px;
width: 100%;
}
.a {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 1;
grid-column-end: 2;
background-color: lightblue;
}
.b {
grid-row-start: 2;
grid-row-end: 3;
grid-column-start: 1;
grid-column-end: 3;
background-color: orange;
}
.c {
grid-row-start: 1;
grid-row-end: 2;
grid-column-start: 2;
grid-column-end: 3;
background-color: lightgreen;
}
<div class="container">
<div class="a cell"></div>
<div class="b cell"></div>
<div class="c cell"></div>
</div>
<p id="output">
</p>
the idea here is to add an extra grid layer beyond the container. lets call it "pseudoContainer".
This pseudoContainer will be absolute postioned.
It will contain elements in columns with 16,6% width and a heigth that will match to the grid.
Now we can bind the mouseenter to the pseudo elements.
Based on #Ian answer I have made a fiddle:
first create some pseudo elements:
var cont = $(".pseudoContainer");
for (var i = 0; i < 18; i++) {
var newDiv = document.createElement("div");
newDiv.setAttribute('class', 'pseudoitem');
newDiv.innerHTML = '<div class="pseudoitem-inner"></div>';
cont.append(newDiv);
}
then add listener:
Array.from(document.getElementsByClassName("pseudoitem-inner")).forEach(element => {
element.addEventListener("mouseenter", () => {
const style = window.getComputedStyle(element);
currentCellPosition = {
"grid-row-start": style.gridRowStart,
"grid-row-end": style.gridRowEnd,
"grid-column-start": style.gridColumnStart,
"grid-column-end": style.gridColumnEnd
}
console.log(currentCellPosition);
})
})
with some styling you can see:
.pseudoContainer{
position:absolute;
top:0;
left:0;
width: 100%;
display: block;
padding: 3px;
z-index:1;
}
.pseudoitem{
width: calc(16.6% - 10px);
height: 126px;
float: left;
padding: 5px 5px 5px;
z-index:2;
}
.pseudoitem-inner{
background-color: red;
height: 100%;
width: 100%;
}
.container{
z-index:1;
}
.grid-stack-item{
z-index:3;
}
now you only to refine the pseudo elements creation process so that they contain the grid information.
here is a fiddle
EDIT:
Now you will get the desired information onmouseover.
The way here is also to add an extra grid layer beyond the container and set position absolute.
Unfortunately this will work only for this grid and it is really not my best work.
In priciple it is the same as solution one but now make also use of grid style here.
var cont = $(".pseudoContainer");
var y,x;
for (var i = 0; i < 18; i++) {
if(i <= 5) y = 0, x = i;
if(i > 5 && i <= 11) y = 1, x = i - 6;
if(i > 11 && i <= 18) y = 2, x = i - 12;
var newDiv = document.createElement("div");
newDiv.setAttribute('class', 'pseudoitem');
newDiv.setAttribute('data-gs-width', '1');
newDiv.setAttribute('data-gs-height', '1');
newDiv.setAttribute('data-gs-x', x);
newDiv.setAttribute('data-gs-y', y);
newDiv.innerHTML = '<div class="pseudoitem-inner"></div>';
cont.append(newDiv);
}
for(let elm of document.getElementsByClassName('pseudoitem')){
elm.style['grid-column-start'] = parseInt(elm.getAttribute('data-gs-x')) + 1;
elm.style['grid-column-end'] = parseInt(elm.getAttribute('data-gs-x')) + parseInt(elm.getAttribute('data-gs-width')) + 1;
elm.style['grid-row-start'] = parseInt(elm.getAttribute('data-gs-y')) +1 ;
elm.style['grid-row-end'] = parseInt(elm.getAttribute('data-gs-y')) + parseInt(elm.getAttribute('data-gs-height')) +1;
}
let currentCellPosition = {
"grid-row-start": null,
"grid-row-end": null,
"grid-column-start": null,
"grid-column-end": null
}
Array.from(document.getElementsByClassName("pseudoitem")).forEach(element => {
element.addEventListener("mouseenter", () => {
const style = window.getComputedStyle(element);
currentCellPosition = {
"grid-row-start": style.gridRowStart,
"grid-row-end": style.gridRowEnd,
"grid-column-start": style.gridColumnStart,
"grid-column-end": style.gridColumnEnd
}
console.log(currentCellPosition);
})
})
and the styling:
.pseudoContainer{
position:absolute;
top:10px;
left:10px;
width: 100%;
height: 400px;
display: grid;
z-index:1;
grid-template-columns: repeat(6, 1fr);
grid-gap: 0px;
}
.pseudoitem{
z-index:2;
}
.pseudoitem-inner{
height: 100%;
width: 100%;
}
.container{
z-index:1;
}
.grid-stack-item{
z-index:3;
}
EDIT2:
I have rewritten the calculation. now you can define "repeat" and "rows" parameter and layer2 grid will be automatically created.
the fiddle is updated and here is the code change:
var cont = $(".pseudoContainer");
var y,x;
var repeat = 6;
var rows = 3;
for (var i = 0; i < rows*repeat; i++) {
if(i%repeat === 0) {
y = i/repeat;
}
x = i- y * repeat;
var newDiv = document.createElement("div");
newDiv.setAttribute('class', 'pseudoitem');
newDiv.setAttribute('data-gs-width', '1');
newDiv.setAttribute('data-gs-height', '1');
newDiv.setAttribute('data-gs-x', x);
newDiv.setAttribute('data-gs-y', y);
newDiv.innerHTML = '<div class="pseudoitem-inner"></div>';
cont.append(newDiv);
}
for(let elm of document.getElementsByClassName('pseudoitem')){
elm.style['grid-column-start'] = parseInt(elm.getAttribute('data-gs-x')) + 1;
elm.style['grid-column-end'] = parseInt(elm.getAttribute('data-gs-x')) + parseInt(elm.getAttribute('data-gs-width')) + 1;
elm.style['grid-row-start'] = parseInt(elm.getAttribute('data-gs-y')) +1 ;
elm.style['grid-row-end'] = parseInt(elm.getAttribute('data-gs-y')) + parseInt(elm.getAttribute('data-gs-height')) +1;
}
let currentCellPosition = {
"grid-row-start": null,
"grid-row-end": null,
"grid-column-start": null,
"grid-column-end": null
}
Array.from(document.getElementsByClassName("pseudoitem")).forEach(element => {
element.addEventListener("mouseenter", () => {
const style = window.getComputedStyle(element);
currentCellPosition = {
"grid-row-start": style.gridRowStart,
"grid-row-end": style.gridRowEnd,
"grid-column-start": style.gridColumnStart,
"grid-column-end": style.gridColumnEnd
}
console.log(currentCellPosition);
})
})
I want to add recursive filters using simple HTML buttons/javascript. Till now, i have only been able to add one level of filter. What i want to do is - allow the user to select the filter in 2 stages. For example, user should be able to filter on experience range "10+" and then further filter on next stage such as "Operations". So this way only profiles that fit 10+ experience and Operations are shown.
I have been able to add filter and logic for one stage. That is, the code works and filters on experience range (5-8, 8-10, 10+), but unable to implement the second stage of filter (IT, Operations etc.)
filterSelection("all")
function filterSelection(c) {
var x, i;
x = document.getElementsByClassName("column");
if (c == "all") c = "";
for (i = 0; i < x.length; i++) {
w3RemoveClass(x[i], "show");
if (x[i].className.indexOf(c) > -1) w3AddClass(x[i], "show");
}
}
function w3AddClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1) {
element.className += " " + arr2[i];
}
}
}
function w3RemoveClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
// Add active class to the current button (highlight it)
var btnContainer = document.getElementById("myBtnContainer");
var btns = btnContainer.getElementsByClassName("btn");
for (var i = 0; i < btns.length; i++) {
btns[i].addEventListener("click", function() {
var current = document.getElementsByClassName("active");
current[0].className = current[0].className.replace(" active", "");
this.className += " active";
});
}
* {
box-sizing: border-box;
}
body {
background-color: #f1f1f1;
padding: 20px;
font-family: Arial;
}
/* Center website */
.main {
max-width: 1000px;
margin: auto;
}
h1 {
font-size: 50px;
word-break: break-all;
}
.row {
margin: 8px -16px;
}
/* Add padding BETWEEN each column */
.row,
.row>.column {
padding: 8px;
}
/* Create four equal columns that floats next to each other */
.column {
float: left;
width: 25%;
display: none;
/* Hide all elements by default */
}
/* Clear floats after rows */
.row:after {
content: "";
display: table;
clear: both;
}
/* Content */
.content {
background-color: white;
padding: 10px;
}
/* The "show" class is added to the filtered elements */
.show {
display: block;
}
/* Style the buttons */
.btn {
border: none;
outline: none;
padding: 12px 16px;
background-color: white;
cursor: pointer;
}
.btn:hover {
background-color: #ddd;
}
.btn.active {
background-color: #666;
color: white;
}
/* Responsive layout - makes a two column-layout instead of four columns */
#media screen and (max-width: 900px) {
.column {
width: 50%;
}
}
/* Responsive layout - makes the two columns stack on top of each other instead of next to each other */
#media screen and (max-width: 600px) {
.column {
width: 100%;
}
}
<!-- MAIN (Center website) -->
<div class="main">
<div id="myBtnContainer">
<button class="btn active" onclick="filterSelection('all')"> Show all</button>
<button class="btn" onclick="filterSelection('5-8')"> 5-8</button>
<button class="btn" onclick="filterSelection('8-10')"> 8-10</button>
<button class="btn" onclick="filterSelection('10+')"> 10+</button>
</div>
<div id="myBtnContainer">
<button class="btn" onclick="filterSelection('Operations')"> Operations</button>
<button class="btn" onclick="filterSelection('Manufacturing')"> Manufacturing</button>
</div>
<!-- Portfolio Gallery Grid -->
<div class="row">
<div class="column 8-10">
<div class="content">
<a href="https://xxx" target="_blank">
<img src="https:xxx" alt="Bagish" style="width:100%" ;height:auto;>
<h4>abc</h4>
<p>8+ Experience in Oil&Gas, Manufacturing</p>
</div>
</div>
<div class="column 10+">
<div class="content">
<a href="https://xxx">
<img src="xxx" alt="def" style="width:100%" ;height:auto;>
<h4>def</h4>
<p>10+ Experience in Oil&Gas, Manufacturing</p>
</div>
</div>
<div class="column 10+">
<div class="content">
<a href="https://abc">
<img src="abc" ;height:auto;>
<h4>ghi</h4>
<p>13+ Experience in IT Program Management</p>
</div>
</div>
<!-- END GRID -->
</div>
<!-- END MAIN -->
</div>
Please help me add another level of filter for IT/Operations etc.
A bit adjusted and simplified, but suppose works as you wanted ;-)
In case you are happy, mind pressing accept button (under voting).
var filtersList = {};
filterSelection(document.getElementsByName("myBtnContainer")[0].children[0]);
function renameAll(i) {
return i.replace('Show all', 'all');
}
function filterSelection(current) {
var x, i;
var c = renameAll(current.innerText.trim());
var buttons = document.getElementsByName("myBtnContainer");
var group = current;
do {
group = group.parentElement;
if (group.getAttribute("name") == "myBtnContainer") {
for (i = 0;i<buttons.length;i++) {
if (buttons[i] === group) {
group = i + 1;
break;
}
}
break;
}
} while (true);
var has = filtersList[c];
if (!has) {
if (filtersList.all && group == 1) filtersList = {};
if (c == 'all') filtersList = { all:1 }
else filtersList[c] = group;
} else {
delete filtersList[c];
if (c == 'all') filtersList = {};
}
var records = document.getElementsByClassName("column");
var matched = 0, filtersNo = [null,null];
updateButtons(buttons, filtersNo);
for (i = 0; i < records.length; i++) {
w3RemoveClass(records[i], "show");
var matchedFilters = [0,0];
for(var c1 in filtersList) {
var group = filtersList[c1]-1;
if (c1 == "all") c1 = "";
if (records[i].className.indexOf(c1) > -1) {
matchedFilters[group]++;
}
}
// Display trick here 2 binary numbers or logic table - each bit means non-zero number in group (matched > 0 / selected filter > 0) - hope works properly ;-)
var matched = (!!matchedFilters[0]) + (!!matchedFilters[1])*2;
var filters = (!!filtersNo[0]) + (!!filtersNo[1])*2;
if ((matched & filters) == filters && (filters != 0)) {
w3AddClass(records[i], "show");
}
}
updateButtons(buttons, filtersNo);
}
function updateButtons(buttons, filtersNo) {
filtersNo[0] = 0;
filtersNo[1] = 0;
for (i = 0;i<buttons.length;i++) {
if (i && !filtersNo[0]) filtersList = {};
for (var j = 0;j<buttons[i].children.length;j++) {
var el = buttons[i].children[j];
c = renameAll(el.innerHTML.trim());
if (filtersList[c]) {
filtersNo[i]++;
if (el.className.indexOf("active") < 0) el.className += " active";
} else {
el.className = el.className.replace(/[ ]*active[ ]*/,'');
}
}
}
if (filtersNo[0]) {
buttons[1].style.display = "";
} else {
buttons[1].style.display = "none";
}
}
function w3AddClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
if (arr1.indexOf(arr2[i]) == -1) {
element.className += " " + arr2[i];
}
}
}
function w3RemoveClass(element, name) {
var i, arr1, arr2;
arr1 = element.className.split(" ");
arr2 = name.split(" ");
for (i = 0; i < arr2.length; i++) {
while (arr1.indexOf(arr2[i]) > -1) {
arr1.splice(arr1.indexOf(arr2[i]), 1);
}
}
element.className = arr1.join(" ");
}
* {
box-sizing: border-box;
}
body {
background-color: #f1f1f1;
padding: 20px;
font-family: Arial;
}
/* Center website */
.main {
max-width: 1000px;
margin: auto;
}
h1 {
font-size: 50px;
word-break: break-all;
}
.row {
margin: 8px -16px;
}
/* Add padding BETWEEN each column */
.row,
.row>.column {
padding: 8px;
}
/* Create four equal columns that floats next to each other */
.column {
float: left;
width: 25%;
display: none;
/* Hide all elements by default */
}
/* Clear floats after rows */
.row:after {
content: "";
display: table;
clear: both;
}
/* Content */
.content {
background-color: white;
padding: 10px;
}
/* The "show" class is added to the filtered elements */
.show {
display: block;
}
/* Style the buttons */
.btn {
border: none;
outline: none;
padding: 12px 16px;
background-color: white;
cursor: pointer;
}
.btn:hover {
background-color: #ddd;
}
.btn.active {
background-color: #666;
color: white;
}
/* Responsive layout - makes a two column-layout instead of four columns */
#media screen and (max-width: 900px) {
.column {
width: 50%;
}
}
/* Responsive layout - makes the two columns stack on top of each other instead of next to each other */
#media screen and (max-width: 600px) {
.column {
width: 100%;
}
}
<div class="main">
<div name="myBtnContainer">
<button class="btn" onclick="filterSelection(this)"> Show all</button>
<button class="btn" onclick="filterSelection(this)"> 5-8</button>
<button class="btn" onclick="filterSelection(this)"> 8-10</button>
<button class="btn" onclick="filterSelection(this)"> 10+</button>
</div>
<div name="myBtnContainer" style="display: none">
<button class="btn" onclick="filterSelection(this)"> Operations</button>
<button class="btn" onclick="filterSelection(this)"> Manufacturing</button>
</div>
<!-- Portfolio Gallery Grid -->
<div class="row">
<div class="column 8-10 Operations">
<div class="content">
<a href="https://mbaex2020.co/batchprofiledetails#7839de77-db6d-4c0e-9072-d60fb35d09c9" target="_blank">
<img src="https://img1.wsimg.com/isteam/ip/97d4b213-5779-4c13-95cf-100a6b778fe3/Bagish.jpg/:/rs=w:600,h:750,cg:true,m/cr=w:1200,h:750,a:cc" alt="Bagish" style="width:100%" ;height:auto;>
<h4>Bagish</h4>
<p>8+ Experience in Oil&Gas, Manufacturing</p>
</div>
</div>
<div class="column 10+ Operations">
<div class="content">
<a href="https://mbaex2020.co/batchprofiledetails#c55a69ed-2170-41cf-b71d-e6546bb77c14" target="_blank">
<img src="https://img1.wsimg.com/isteam/ip/97d4b213-5779-4c13-95cf-100a6b778fe3/Rahul.jpg/:/rs=w:600,h:750,cg:true,m/cr=w:1200,h:750,a:cc" alt="Rahul" style="width:100%" ;height:auto;>
<h4>Rahul</h4>
<p>10+ Experience in Oil&Gas, Manufacturing</p>
</div>
</div>
<div class="column 10+">
<div class="content">
<a href="https://mbaex2020.co/batchprofiledetails#c26922ff-3ee8-4d1e-92bf-974f46061d40" target="_blank">
<img src="https://img1.wsimg.com/isteam/ip/97d4b213-5779-4c13-95cf-100a6b778fe3/Nikhil.jpg/:/rs=w:600,h:750,cg:true,m/cr=w:1200,h:750,a:cc" alt="Nikhil" style="width:100%" ;height:auto;>
<h4>Nikhil</h4>
<p>13+ Experience in IT Program Management</p>
</div>
</div>
<!-- END GRID -->
</div>
<!-- END MAIN -->
</div>
I don't know how to describe this without making it more complicated.
So look at the result of the code and click on the first link with "Show", then the second one and third one.
When the second link is clicked, first one closes but text remains "Hide" and i want it to change to "Show".
So, when clicking a link, detect if any other link has text "Hide" and change it to "Show".
And please no jQuery...
document.getElementsByClassName("show")[0].onclick = function() {
var x = document.getElementsByClassName("hide")[0];
var y = document.getElementsByClassName("show")[0];
if (x.classList.contains("visible")) {
x.classList.remove("visible");
y.textContent = "Show";
} else {
closeOther();
x.classList.add("visible");
y.textContent = "Hide";
}
};
document.getElementsByClassName("show")[1].onclick = function() {
var x = document.getElementsByClassName("hide")[1];
var y = document.getElementsByClassName("show")[1];
if (x.classList.contains("visible")) {
x.classList.remove("visible");
y.textContent = "Show";
} else {
closeOther();
x.classList.add("visible");
y.textContent = "Hide";
}
};
document.getElementsByClassName("show")[2].onclick = function() {
var x = document.getElementsByClassName("hide")[2];
var y = document.getElementsByClassName("show")[2];
if (x.classList.contains("visible")) {
x.classList.remove("visible");
y.textContent = "Show";
} else {
closeOther();
x.classList.add("visible");
y.textContent = "Hide";
}
};
function closeOther() {
var visible = document.querySelectorAll(".visible"),
i, l = visible.length;
for (i = 0; i < l; ++i) {
visible[i].classList.remove("visible");
}
}
.style {
background-color: yellow;
width: 200px;
height: 200px;
display: inline-block;
}
.hide {
background-color: red;
width: 50px;
height: 50px;
display: none;
position: relative;
top: 50px;
left: 50px;
}
.hide.visible {
display: block;
}
<div class="style">
Show
<div class="hide">
</div>
</div>
<div class="style">
Show
<div class="hide">
</div>
</div>
<div class="style">
Show
<div class="hide">
</div>
</div>
I tried to write a solution which didn't use any javascript at all and worked using CSS alone. I couldn't get it to work though - CSS can identify focus but it can't identify blur (ie. when focus has just been removed).
So here is a solution which uses javascript and the classList API, instead:
var divs = document.getElementsByTagName('div');
function toggleFocus() {
for (var i = 0; i < divs.length; i++) {
if (divs[i] === this) continue;
divs[i].classList.add('show');
divs[i].classList.remove('hide');
}
this.classList.toggle('show');
this.classList.toggle('hide');
}
for (let i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', toggleFocus, false);
}
div {
display: inline-block;
position: relative;
width: 140px;
height: 140px;
background-color: rgb(255,255,0);
}
.show::before {
content: 'show';
}
.hide::before {
content: 'hide';
}
div::before {
color: rgb(0,0,255);
text-decoration: underline;
cursor: pointer;
}
.hide::after {
content: '';
position: absolute;
top: 40px;
left: 40px;
width: 50px;
height: 50px;
background-color: rgb(255,0,0);
}
<div class="show"></div>
<div class="show"></div>
<div class="show"></div>
Like this?
Just added following to closeOther():
visible = document.querySelectorAll(".show"),
i, l = visible.length;
for (i = 0; i < l; ++i) {
visible[i].textContent="Show";
}