I am trying to make something similar to what you find in google images. When a picture is clicked, a div with the image appears on the next line over the other images that is under the clicked one.
I have a set of divs with float:left and position:relative. They have different widths. When i click on a div i want a new full width div to appear on the next line. The divs under the clicked one should be bushed down under the full width one.
I tried to do this by looping through the divs and compare the position of the divs to the clicked one like this:
$(".Wrapper").on("click", ".testDivs", function () {
var thisTop = $(this).position().top;
$(".testDivs").each(function(i, obj) {
var otherTop = $(obj).position().top;
if(thisTop < otherTop){
$(".fullWidthDiv").insertBefore(obj);
return;
}
});
});
This doesn't work and I don't really know how I should do this. Any tips/solutions?
This requires a lot of information to explain. So I'd rather suggest reading a blog post on this topic.Hope this will help you.
https://www.sitepoint.com/recreating-google-images-search-layout-css/
Here is a way to achieve that. It will not keep scroll position but that would be another easy fix.
<div class="wrapper">
<div class="row1 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
<div class="row2 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
<div class="row3 row">
<div class="img1 img"></div>
<div class="img2 img"></div>
<div class="img3 img"></div>
</div>
</div>
I only applied some styling ti increase visibility of the changes.
.img {
width: 32%;
height: 100px;
background-color: #ccc;
display: inline-block;
}
.row {
border: 1px solid green
}
.big-img {
height: 300px;
}
And finally the JS:
$('.img').click(function() {
var expandedImg = '<div class="big-img"></div>';
$('.big-img').remove();
$(this).parent().append(expandedImg);
})
Fiddle: https://jsfiddle.net/a5fm2dup/
why don't you just do
$(".Wrapper").on("click", ".testDivs", function () {
$(this).after($(".fullWidthDiv"));
});
Ended up making a solution based on my initial code. This doesn't require all the CSS the other solution that was postet suggested. Adding rows dynamically was also suggested, but this became very complicated when making it responsive to window resizing. Feel free to reply if you have any comments on this
function positionDiv() {
var checker = false;
var n;
var thisTop = clickedElement.position().top;
$(".testDivs").each(function(i, obj) {
var otherTop = $(obj).position().top;
if(thisTop < otherTop){
$(".testDivs:eq(" + (i-1) + ")").after($(".fullWidthDiv"));
checker = true;
return false;
}
n = i;
});
if (!checker) {
$(".testDivs:eq(" + n + ")").after($(".fullWidthDiv"));
}
}
var clickChecker = null;
$(".wrapper").on("click", ".testDivs", function () {
if (clickChecker === this){
$(".fullWidthDiv").hide();
clickChecker = null;
} else {
$(".fullWidthDiv").show();
clickChecker = this;
}
clickedElement = $(this);
positionDiv();
});
window.onresize = function(event) {
if( clickedElement != null) {
$(".tagTextWrapper").hide();
positionDiv();
$(".tagTextWrapper").show();
}
}
Related
I'm trying the count the total number of divs clicked and exactly which ones were clicked. I'm using an event listener because the onclick is already used. Let me clarify a bit more, first, here's my code:
<div class="wrapper">
<div class="square" onclick="classList.toggle('selected')">1</div>
<div class="square" onclick="classList.toggle('selected')">2</div>
<div class="square" onclick="classList.toggle('selected')">3</div>
</div>
<div id="dis"></div>
.selected {
background: white;
}
var numClicked = document.querySelectorAll('.wrapper');
numClicked.forEach(numClicked =>
numClicked.addEventListener('click', clickedDivs)
)
function clickedDivs () {
i = 0;
numClicked.forEach(numClicked =>
i++
var x = document.getElementById("dis");
x.innerHTML = "Squares selected: " + i;
}
What I'm trying to do with my javascript is count how many divs are selected. I'm also trying to tell exactly where ones were clicked. Let's say 1 and 2 were clicked, how do I find those were clicked and total number of divs clicked using js?
What you are doing wrong here is:
You are initialising i within the onClick event fn. which will always reset the value to 0 when ever the div will be clicked.
you are not storing anywhere which div is clicked
You are adding you'r listener on wrapper instead of .square (if you are not trying to get the value of clicked wrappers instead of clicked square)
So you can modify you'r javascript like this
<style>
.square{width: 100px; height: 100px; background: grey;}
.selected {
background: white;
}
</style>
<div class="wrapper">
<div class="square" onclick="classList.toggle('selected')">1</div>
<div class="square" onclick="classList.toggle('selected')">2</div>
<div class="square" onclick="classList.toggle('selected')">3</div>
</div>
<div id="dis"></div>
<script>
var numClicked = document.querySelectorAll('.square');
numClicked.forEach(numClick => {
numClick.addEventListener('click', clickedDivs)
}
)
var itemsClicked = [] //to store which div is clicked
function clickedDivs (e) {
var value = e.target.innerHTML;
//edit
if(itemsClicked.indexOf(value) != -1) itemsClicked.splice(itemsClicked.indexOf(value), 1)
else
itemsClicked.push(value);
var x = document.getElementById("dis");
x.innerHTML = "Squares selected: " + itemsClicked.join(",");
}
</script>
edit:
added to code to remove data from the list if already exist.
Rather than attach a handler to each div, you can use 1 window event listener. Give each clickable div an id that contains "clickable" so the event listener can filter out divs you aren't tracking. When you first click a tracked div, set its id as a key within a global object and assign 1 as the value; on additional clicks, increase value by 1.
const clicks = {};
window.addEventListener("click", (e)=> {
const id = e.target.id;
if(!id.includes("clickable"))return;
clicks[id]? clicks[id] += 1 : clicks[id] = 1;
console.log(clicks);
},)
<div class="wrapper">
<div id="clickable1" class="square">1</div>
<div id="clickable2" class="square">2</div>
<div id="clickable3" class="square">3</div>
</div>
My solution, I haven't tested it yet, test it and tell me how we adjusted it.
<div class="wrapper">
<div class="square" id="d-1">1</div>
<div class="square" id="d-2">2</div>
<div class="square" id="d-3">3</div>
</div>
<div id="result"></div>
var count = [];
var wrappers = document.querySelectorAll('.wrapper');
wrappers.forEach(square => square.addEventListener('click',() => onClickwrapperSquare(square.id));
function onClickwrapperSquare(id) {
var result = document.getElementById('result');
if(count.indexOf(id) == -1){
count.push(id);
}else{
count = count.slice(count.indexOf(id)+ 1);
}
result.innerHTML = `Squares selected: ${count.length}`;
}
This can be simply achieved by jQuery.
var count;
$(".square").click(function (){
count = count+1;
$("#dis").html(count);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="wrapper">
<div class=square">1</div>
<div class="square">2</div>
<div class="square">3</div>
</div>
<div id="dis"></div>
I have two div's within a parent div. I need to change the classes for the child div which I clicked. For that I am writing a method to check which child was clicked and respectively I am trying to hide the other child div.
But I am not able to add classes or remove classes since the index is showing always as undefined. I am feeling there is some problem with the return statement.
function changeClass() {
const list = document.getElementById('my_div').children;
const indx = this.getIndexOfParent(list);
for (let i = 0; i < list.length; i++) {
if (indx === 0) {
list[indx + 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
} else if (indx === 1) {
list[indx - 1].classList.add("d-none d-sm-block");
list[indx].classList.remove("col-6 d-none d-sm-block");
}
list[indx].classList.add("d-xs-block");
}
}
function getIndexOfParent(child_list) {
for (var i = 0, len = child_list.length; i < len; i++) {
((index) => {
child_list[i].onclick = () => {
return index;
};
})(i);
}
}
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row > div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-1
</div>
<div onclick="changeClass()" class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
All I want is that, when I click on child-div-1 it should hide child-div-2 and vice versa only for small screens (which is why I am handling it by col-6 and d-xs-block classes)
Can anyone help me to solve the below problem.
You have added onclick within the for loop. Instead add the class to the clicked child div and remove the class from it's sibling div.
document.querySelectorAll('div.row > div')
.forEach((div) => {
div.addEventListener('click', function({
target
}) {
target.classList.add('d-none', 'd-sm-block');
const sibDiv = Array.prototype.filter.call(target.parentNode.children, div => div != target)[0];
sibDiv.classList.remove('col-6', 'd-none', 'd-sm-block');
});
});
.row {
background: #f8f9fa;
margin-top: 20px;
}
.row>div {
border: solid 1px black;
padding: 10px;
}
<div class="container">
<div class="row">
<div class="col-md-6 col-6">
child-div-1
</div>
<div class="col-md-6 col-6">
child-div-2
</div>
</div>
</div>
-- Edit --
The return statement will return the value of index to the callback function, you also need to add return to the callback function, so whatever result the callback function get will return to the function getIndexOfParent.
function getIndex() {
let i = 0;
((index) => { // No return, logs undefined
return index;
})(i);
}
console.log(getIndex());
function getIndex() {
let i = 0;
return ((index) => { // with return
return index;
})(i);
}
console.log(getIndex());
I made a pen for solving this problem.
Check the pen here
the solution is easy you simply have to write this line
e.stopPropagation();
this will stop the event from triggering on parent divs
basically what you are describing is called event bubbling.
you can read about it more on medium
I would like to create, using the setInterval() JS function, a visual effect that displays text one character at the time with an interval of 100ms per character, on an Angular application.
Note that this happens in the index.html within the <app-root> tags, so it will appear only while the app is bootstrapped.
After reading the setInterval() page i thought that this would make the job, so this is my code:
var divs=['rBox','aBox','tBox','sBox'];
var index=-1;
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
var fun = setInterval(display,100);
}
function display(){
if(index < 4){
document.getElementById(divs[++index]).style.visibility='visible'
}else{
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
But it does not display anything, the divs containing the numbers are there with visibility set to hidden but it seems like they are never set to visible
I can't see where the problem lies. If I look at the code from an algorithmic point of view, I guess I probably don't understand very well the inner working of setInterval().
fun was not declared globally
And index was incremented too much
A rough update to the code:
var divs=['rBox','aBox','tBox','sBox'];
var index=-1;
var fun
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
fun = setInterval(display,100);
}
function display(){
if(index < 3){
document.getElementById(divs[++index]).style.visibility='visible'
}else{
clearInterval(fun);
}
}
displayChars()
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
After minor modifications it's working, see below. I changed index to start at 0 and to be used with divs[index++] (so use-then-increment), and to compare it with divs.length instead of hardcoded 4. Also I put variable fun at the global level.
var divs = ['rBox', 'aBox', 'tBox', 'sBox'];
var index = 0;
var fun = -1; // timer handle
function displayChars() {
for (container of divs) {
document.getElementById(container).style.visibility = 'hidden';
}
fun = setInterval(display, 100);
}
function display() {
if (index < divs.length) {
document.getElementById(divs[index++]).style.visibility = 'visible'
} else {
clearInterval(fun);
}
}
displayChars();
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
Your variable fun needs to be global, otherwise display() can't
access it
Don't forget to declare container in your for...of loop as an actual variable
You where incrementing index too often
var divs = ['rBox', 'aBox', 'tBox', 'sBox'];
var index = -1;
var fun;
function displayChars() {
for (var container of divs) {
document.getElementById(container).style.visibility = 'hidden';
}
fun = setInterval(display, 100);
}
function display() {
if (index < 3) {
document.getElementById(divs[++index]).style.visibility = 'visible';
} else {
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
Pure CSS solution, if an option, looks something like this:
.rats-div > div {
opacity: 0; /* or "visibility: hidden" */
animation: opacity .1s forwards;
}
.rats-div > div:nth-child(2) {animation-delay: .1s}
.rats-div > div:nth-child(3) {animation-delay: .2s}
.rats-div > div:nth-child(4) {animation-delay: .3s}
#keyframes opacity {
to {opacity: 1} /* or "visibility: visible / initial" */
}
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
var divs=['rBox','aBox','tBox','sBox'];
var index=0;
var fun=null;
function displayChars(){
for(container of divs){
document.getElementById(container).style.visibility='hidden';
}
fun = setInterval(display,100);
}
function display(){
if(index < 4){
document.getElementById(divs[index]).style.visibility='visible'
index++;
}else{
clearInterval(fun);
}
}
displayChars();
<app-root>
<div class="rats-div">
<div id="rBox">1</div>
<div id="aBox">2</div>
<div id="tBox">3</div>
<div id="sBox">4..</div>
</div>
</app-root>
try this code. it should do your job.
How can I affect an element when another element is hovered, and the two elements are in this structure:
<div id="parent_element">
<div id="class-open-1"></div>
<div id="class-close-1"></div>
</div>
or they might be in this structure:
<div id="parent_element">
<div id="div1">
<div id="class-open-1"></div>
</div>
</div>
<div id="parent_element"></div>
<div id="div2">
<div id="class-close-1"></div>
</div>
</div>
I have tried this solution which works perfectly for the first case, but does not work for the second case:
_style.innerHTML = ".class-open" + j + ":hover{ background-color: cyan; } .class-open-" + j + ":hover ~ .class-close-" + j + " { background-color: cyan; }
the j changes , so I am only hovering the classnames that have the same j
this solution works for the case one, but doesnt work for both cases.
I have also tried this :
_style.innerHTML = ".class-open" + j + ":hover{ background-color: cyan; } .class-open-" + j + ":hover .class-close-" + j + " { background-color: cyan; }
But this changes the background-color, and doesn't only hover.
I only need css or javascript to solve this, any suggestions?
I am looking for a solution that works for BOTH cases.
You're going to need to use JavaScript mouseover or jQuery .hover(). This shows mouseover from the MDN.
myElement.addEventListener("mouseover", (event) => {
// do something to the other element.
}
Try this friend:
div#one
{
background-color:red;
}
div#one:hover ~ div#two
{
background-color:yellow;
}
<div id="one">
ONEE
</div>
<div>
SIMPLE DIV
</div>
<div>
SIMPLE DIV
</div>
<div>
SIMPLE DIV
</div>
<div>
SIMPLE DIV
</div>
<div>
SIMPLE DIV
</div>
<div id="two">
two
</div>
if (/\bclass-/.test(target.className)) {
var _style = document.createElement('style');
var j = target.className.match(/\d+$/)[0];
target.style.backgroundColor = "red";
_style.innerHTML = ".class-close-" + j + " { background-color: red; }";
setTimeout(function () {
target.style.backgroundColor = "";
_style.innerHTML = '.class-close-' + j + ' { background-color: ""; }';
}, 500);
document.head.appendChild(_style);
}
Here is the solution I made, but still looking for the "good effect" of Hover instead of just deleting the style after the 500ms..
Hope this helps someone.
If you can control the template code [which I believe we do in most cases], then if one element is inside other element, it can be solved just using css.
Else, if they have different parent, JS would be the direction which people have already suggested.
I am attempting to implement a scroll function where the CSS of the inner div's change when it reaches a certain height from the top.
var $container = $(".inner-div");
var containerTop = $container.offset().top;
var documentTop = $(document).scrollTop();
var wHeight = $(window).height();
var minMaskHeight = 0;
var descriptionMax = 200;
var logoMin = -200;
var maskDelta = descriptionMax - minMaskHeight;
var $jobOverview = $container.find(".right");
var $jobLogo = $container.find(".left");
var curPlacementPer = ((containerTop - documentTop) / wHeight) * 100;
var topMax = 85;
var center = 20;
var bottomMax = -15;
//console.log("Placement: " + curPlacementPer);
function applyChanges(perOpen) {
var maskHeightChange = maskDelta * (perOpen / 100);
var opacityPer = perOpen / 100;
var newDescriptionLeft = descriptionMax - maskHeightChange;
var newLogoLeft = logoMin + maskHeightChange;
if (newDescriptionLeft <= 0) newDescriptionLeft = 0;
if (newLogoLeft >= 0) newLogoLeft = 0;
if (opacityPer >= 1) opacityPer = 1;
$jobOverview.css({
transform: "translate(" + newDescriptionLeft + "%,-50%)",
opacity: opacityPer
});
$jobLogo.css({
transform: "translate(" + newLogoLeft + "%,-50%)",
opacity: opacityPer
});
}
if (window.innerWidth > 640) {
$container.removeClass("mobile");
// console.log("Placement: " + curPlacementPer);
if (curPlacementPer <= topMax /*&& curPlacementPer >= center*/ ) {
var perOpen = ((topMax - curPlacementPer) / 25) * 100;
applyChanges(perOpen);
} else if (curPlacementPer < center /*&& curPlacementPer >= bottomMax*/ ) {
var perOpen = (((bottomMax - curPlacementPer) * -1) / 25) * 100;
applyChanges(perOpen);
} else {
$jobOverview.css({
transform: "translate(200%,-50%)",
opacity: "0"
});
$jobLogo.css({
transform: "translate(-300%,-50%)",
opacity: "0"
});
}
<div class="outer-div">
<div class="inner-div first">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div second">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div third">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div fourth">
<div class="left"></div>
<div class="right"></div>
</div>
</div>
Currently, all of the inner div's gets changed at the same time.
I noticed that when I change the $container class to equal '.first' and specify it more, it works.
Is there any way to make the inner div's change separately, relative to its height from the top? Any way I can iterate the scroll function so I can add more inner div's in the future and not have to worry about changing my scroll function?
In raw JavaScript, this is my answer:
// Define the element -- The '#fooBar' can be changed to anything else.
var element = document.querySelector("#fooBar");
// Define how much of the element is shown before something happens.
var scrollClipHeight = 0 /* Whatever number value you want... */;
// Function to change an element's CSS when it is scrolled in.
const doSomething = function doSomething() {
/** When the window vertical scroll position plus the
* window's inner height has reached the
* top position of your element.
*/
if (
(window.innerHeight + window.scrollY) - (scrollClipHeight || 0) >=
element.getBoundingClientRect().top
)
// Generally, something is meant to happen here.
element.style = "/* Yay, some CSS! */"
};
// Call the function without an event occurring.
doSomething();
// Call the function when the 'window' scrolls.
addEventListener("scroll", doSomething, false)
This is the method I use. If there are other methods, I'd love to see them as well but this is my answer for now.
consider using 3rd party jQuery plugin for easier job, like one of these:
https://github.com/xobotyi/jquery.viewport
or
https://github.com/zeusdeux/isInViewport
then you can have additional element selector e.g.: ":in-viewport"
so you can:
$(window).on('scroll',function() {
$('div').not(':in-viewport').html('');
$('div:in-viewport').html('hello');
});
Check if current scroll offset from top is bigger than the element offset from the top:
$(window).scroll(function() {
var height = $(window).scrollTop();
var element = $('#changethis'); //change this to your element you want to add the css to
if(height > element.offset().top) {
element.addClass('black'); //add css class black (change according to own css)
}
});
Html:
<div id="changethis">Test</div>
Css:
body
{
height:2000px;
}
.black
{
background-color:black;
color:white;
padding:20px;
}
Demo:
https://codepen.io/anon/pen/WZdEap
You could easily implement this in your existing code.
Below is the sample snippet code, Hope it'll work for you:
$(document).ready(function(){
topMax = 100;
topMin = 25;
$(document).scroll(function(){
$('.inner-div').each(function(){
if($(this).offset().top-$(window).scrollTop()<=topMax && $(this).offset().top-$(window).scrollTop()>=topMin){
$(this).css({'background':'#c7c7c7'});
}else{
$(this).css({'background':'inherit'});
}
});
});
});
div{
width:100%;
border:1px solid red;
padding:5px;
}
div.inner-div{
border: 1px dashed green;
height: 100px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="outer-div">
<div class="inner-div first">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div second">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div third">
<div class="left"></div>
<div class="right"></div>
</div>
<div class="inner-div fourth">
<div class="left"></div>
<div class="right"></div>
</div>
</div>
Happy to help you! :)