I'm trying to add sticky navigation to my website that will change as it scrolls over different sections. When scrolling over a section with the class .dark, it should change the logo and text colour to white.. otherwise black.
The javascript I've been using is below but this only seems to apply to the first element with the class .dark, how can I adapt this to target all elements with the same class?
window.addEventListener('scroll', function () {
var section = document.querySelector('.dark').getBoundingClientRect(),
logo = document.querySelector('#logo-container').getBoundingClientRect();
if (section.top <= logo.top + logo.height && section.top + section.height > logo.top) {
document.getElementById('logo-container').classList.add('white-logo');
document.getElementById('navholder').style.color = "#fff";
} else {
document.getElementById('logo-container').classList.remove('white-logo');
document.getElementById('navholder').style.color = "#111";
}
});
I apologise if this is an obvious question, I'm not too knowledgeable when it comes to javascript! I've tried to look for a solution to this but have not had much success.. any help would be massively appreciated.
If you break this out into several functions, it makes life easier. You can check if the logo is is any of the sections, and then set its class accordingly:
const setLogoBlackStatus = status => {
if (status) {
document.getElementById('logo-container').classList.add('black-logo');
document.getElementById('logo-container').classList.remove('white-logo');
} else {
document.getElementById('logo-container').classList.add('white-logo');
document.getElementById('logo-container').classList.remove('black-logo');
}
}
const logoIsInSection = logo => sectionRect => sectionRect.top <= logo.top + logo.height &&
sectionRect.top + sectionRect.height > logo.top
window.addEventListener('scroll', function() {
var sectionRects = [...document.querySelectorAll('.dark')]
.map(el => el.getBoundingClientRect());
var logo = document.querySelector('#logo-container').getBoundingClientRect();
var logoInAnySections = sectionRects
.some(logoIsInSection(logo))
setLogoBlackStatus(!logoInAnySections);
});
img {
width: 50px;
position: fixed;
top: 20vw;
left: 20vw;
z-index: 1;
}
.white-logo {
filter: invert(90%);
}
.section {
width: 100%;
height: 300px;
}
.dark {
background-color: rgba(20, 20, 30);
}
.white {
background-color: white;
}
<img src="https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/cc.svg" id="logo-container"/>
<div class="section white"></div>
<div class="section dark"></div>
<div class="section white"></div>
<div class="section dark"></div>
Related
I need everyone's help. I currently need to implement a marquee effect. The yellow box needs to be scrolled up to show the name. Every time I scroll, I have to stay in the middle of the box for 1 second before continuing to scroll. I can find such an example on the Internet. , but the logic of this program is a bit difficult for me to understand for urban beginners. I wonder if anyone would like to provide a simpler and easier-to-understand writing method if I want to achieve this marquee effect?
Sorry, I am a beginner in the program, the current logic More complex programs are more difficult to understand.
function slideLine(box, stf, delay, speed, h) {
var slideBox = document.getElementById(box);
var delay = delay || 1000,
speed = speed || 20,
h = h || 40;
var tid = null,
pause = false;
var s = function() {
tid = setInterval(slide, speed);
};
var slide = function() {
if (pause) return;
slideBox.scrollTop += 1;
if (slideBox.scrollTop % h == 0) {
clearInterval(tid);
slideBox.appendChild(slideBox.getElementsByTagName(stf)[0]);
slideBox.scrollTop = 0;
setTimeout(s, delay);
}
};
setTimeout(s, delay);
}
slideLine("kanban_info", "p", 1000, 25, 40);
.kanban {
position: absolute;
margin: 0 auto;
width: 278px;
height: 50px;
background-color: yellow;
left: 50%;
transform: translate(-50%);
text-align: center;
line-height: 6;
}
.kanban .kenban_wrap {
height: 38px;
transform: translateY(28px);
overflow: hidden;
}
.kanban .kenban_wrap .kanban_info {
line-height: 38px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="kanban">
<div class="kenban_wrap" id='kanban_info'>
<p class="kanban_info">Allen</p>
<p class="kanban_info">james</p>
<p class="kanban_info">jack</p>
</div>
</div>
By combining scroll-behavior with anchor tags that are programmatically clicked you can simplify it. This should be easier to understand and you can go from there, even if it might not be the best solution.
let links = document.querySelectorAll("a"); // List of links
let div = document.querySelector("div");
let index = 0;
let t = 2000; // setTimeout duration
// Change Scroll behavior to prevent the animation from the last to first list item
function scrollBeh() {
if(index == 1) {
div.style.scrollBehavior = "auto";
t = 0; // Timeout duration to 0 to prevent `1` being shown longer than other list items
} else {
div.style.scrollBehavior = "smooth";
t = 2000;
}
}
// Loop through list items
function resetInd() {
if(index < 3) {
index++;
} else {
index = 0;
}
}
function clickLinks() {
links[index].click();
resetInd();
scrollBeh();
setTimeout(clickLinks, t);
}
setTimeout(clickLinks, t);
div {
width: 200px;
height: 100px;
background-color: darkblue;
overflow: hidden;
scroll-behavior: smooth;
}
li {
height: 100px;
list-style: none;
}
a {
text-decoration: none;
color: #FFF;
font-size: 50px;
}
<div>
<ul>
<li id="one">1</li>
<li id="two">2</li>
<li id="three">3</li>
<li id="one_loop">1</li>
</ul>
</div>
I have a header with a transparent backgrounsd, and I am trying to get the text of the header to change colour between white and black depending on the background of the div it's overlapping.
So far I have managed to add a class of .color-menu to all the divs where I want the header to be black.
I then have it add a class of .dark-menu to the header when the .color-menu div reaches the top of the page.
The problem is that it only works for the first .colour-menu div. It will change to black when it is in the viewport and back to white for the next div but then when the next .color-menu div gets to the top it doesn't change.
So, it seems like the .each function isn't working but I am not sure how to fix it.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
UPDATE: I have also tried using $(this) but it really throws off when it changes color.
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(this).offset().top;
var bottom_of_element = $(this).offset().top + $(this).outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
Here is a simplified version of my code as an example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
});
$(window).scroll(function() {
$('.color-menu').each(function(i){
var top_of_element = $(".color-menu").offset().top;
var bottom_of_element = $(".color-menu").offset().top + $(".color-menu").outerHeight();
var top_of_screen = $(window).scrollTop();
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0,0,0,0);
margin: 0;
padding:10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.dark-menu a{
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white, .white-bold, .light, .light-bold, .bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class ="black"></div>
<div class ="white"></div>
<div class ="black"></div>
<div class ="white-bold"></div>
<div class ="black"></div>
<div class ="light"></div>
<div class ="black"></div>
<div class ="light-bold"></div>
<div class ="black"></div>
<div class ="bright"></div>
What is happening in your code is that on scroll, you loop through every color-menu div and add the class if it is the current one... but then the code continues to loop though the remaining elements in the array and removes it again because the page is not in the other div.
I've explained step-by-step the changes you need to get this to work after the example, but first you can see it working here:
Working Example:
$(document).ready(function() {
$(".white").addClass("color-menu");
$(".white-bold").addClass("color-menu");
$(".light").addClass("color-menu");
$(".light-bold").addClass("color-menu");
$(".bright").addClass("color-menu");
$(window).scroll(function() {
var inColorMenu = false; /* initialise var to store if we are in color-menu */
var top_of_screen = $(window).scrollTop(); /* just get this once outside loop */
/* Loop through each color-menu element and check if we are in one */
$('.color-menu').each(function(i) {
var top_of_element = $(this).offset().top;
var bottom_of_element = top_of_element + $(this).outerHeight();
/* if we are in a color-menu element, set our var to true and stop processing */
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
});
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
});
});
.header {
width: 100%;
background: rgba(0, 0, 0, 0);
margin: 0;
padding: 10px;
position: fixed;
text-align: center;
}
.header a {
color: white;
font-size: 2rem;
text-transform: uppercase;
}
.header.dark-menu a {
color: black;
}
.black {
background-color: black;
height: 200px;
}
.white,
.white-bold,
.light,
.light-bold,
.bright {
background-color: white;
height: 200px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="header">
<a>This is the header</a>
</div>
<div class="black"></div>
<div class="white"></div>
<div class="black"></div>
<div class="white-bold"></div>
<div class="black"></div>
<div class="light"></div>
<div class="black"></div>
<div class="light-bold"></div>
<div class="black"></div>
<div class="bright"></div>
How this works:
Declare a variable to record whether we are in a "color-menu" class or not, and initialise this to false, e.g.:
var inColorMenu = false;
When looping through $('.color-menu').each, if we are between the top and bottom of one of divs (which your code is already detecting), then set our variable to true to record this.
We can also return false to break the each loop and stop processing the rest of the elements (it will still work without this, we are just reducing the amount of processing required):
if ((top_of_screen > top_of_element) && (top_of_screen < bottom_of_element)) {
inColorMenu = true;
return false; /* N.B. need to return "false" to break from the "each" loop */
}
Finally, after we finish our $('.color-menu').each loop, if inColorMenu is true, we know we are in a color-menu div so we add the dark-menu class to the header, otherwise we remove it:
if (inColorMenu) {
$(".header").addClass("dark-menu");
} else {
$(".header").removeClass("dark-menu");
}
Note: You need to use $(this) when getting the offset().top and outerHeight() so that you are getting the values for the current element in the loop. $(".color-menu") gets the values for an unspecified element with this class so will not work.
I'm trying to change the z-index on the clicked item on vanilla JS.
When a click event happens, the clicked item should come front. There are only two divs atm, but there will be more.
I'm storing the last clicked item, so that if new click event happens the last clicked item have less z-index. But it doesn't seem to work.(both of items are position relative)
Any help would be appreciated.
<div class="post" data-name="post" draggable="true">1</div>
<div class="post" data-name="post" draggable="true">2</div>
const a = new A()
memoSection.addEventListener('click', ({target})=>{
switch(target.dataset.name){
case "post":
let clickedItem ="";
a.bringFront(clickedItem)
break;
})
class A{
constructor(){
this.selected = null
}
bringFront(clickedItem){
this.selected = clickedItem; //store the previously clicked element
if(!clickedItem.style.zIndex == 10 || !clickedItem.style.zIndex){
this.selected.zIndex = 0
clickedItem.style.zIndex = 10
} else {
this.selected.zIndex = 10
clickedItem.style.zIndex = 0 }
}
}
I don't understand what your code was trying to do, but here is example how to get desired effect:
document.querySelectorAll('.post').forEach(item => {
//get all elements with class .post
item.addEventListener('click', event => {
// add Event Listener to each
document.querySelectorAll('.post').forEach(el => {
el.style.zIndex = "0";
})
// when clicked fetch all again and set all back to 0
item.style.zIndex = "10";
//set clicked only to 10
console.clear()
console.log(item.innerHTML);
})
})
.post:first-of-type {
position: absolute;
background-color: blue;
left: 100px;
top: 50px;
width: 150px;
height: 150px;
z-index: 0;
}
.post {
position: absolute;
background-color: red;
left: 25px;
top: 50px;
width: 150px;
height: 150px;
z-index: 0;
}
<div class="post" data-name="post" draggable="true">1</div>
<div class="post" data-name="post" draggable="true">2</div>
I am still fairly new to JS, and I am trying to replace the HTML of a div with a picture that is being moused over, and when the mouse leaves I want it to return to it's normal state. I thought that I did everything right but my code doesn't seem to be working. I've looked through stack overflow and I see a lot of jQuery solutions to my 'problem,' but I would like an answer in pure JavaScript (I'm trying to "maser" this first), along with an explanation so I can understand why the answer IS the answer. Thanks.
I'll try to explain myself (my code). I grabbed reference to the image holder, and I grabbed reference to the the images. I thought I made a function that looped through the array of images and added an event listener to whichever image ( image[i] ) was being moused over. Then, I added an event listener that is supposed to return the image holder to it's default state by inserting the original HTML. I just don't understand how to fix this.
var holder = document.getElementById('holder');
var images = document.getElementsByTagName('img');
var popImage = function () {
for (i = 0; i < images.length; i++) {
images[i].addEventListener('mouseover', = function () {
holder.innerHTML = images[i];
});
images[i].addEventListener('mouseout', function () {
holder.innerHTML =
'<div class='col-md-3 img-fluid' id='img1'><img src='photo1.jpg'></div>
<div class='col-md-3 img-fluid' id='img2'><img src='photo2.jpg'></div>
<div class='col-md-3 img-fluid' id='img3'><img src='photo3.2.jpg'></div>
<div class='col-md-3 img-fluid' id='img4'><img src='photo4.jpg'></div>'
});
};
};
popImage();
You said you are new to JS and just learning which is great but an important part of learning JS is learning when not to use it. As #Yoda said if this was for production you really should use CSS instead of JS.
Here is one way you could accomplish this with pure CSS
<style>
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
float: left;
}
.holder:hover > .img {
opacity: 0;
}
.holder:hover > .img:hover {
opacity: 1;
}
</style>
<div class="holder">
<!-- Using div.img for simplicity, these whould be your <img/> tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
For the purpose of learning, here's how you'd do it in JS:
var holder = document.getElementById('holder');
var images = document.querySelectorAll('.img');
var filter = false;
function popImage () {
// Use for (var i = 0 . . .
// Instead of for (i = 0 . . .
// Because without var, i will be stored in the global scope
for (var i = 0; i < images.length; i++) {
(function (_i) {
images[_i].addEventListener('mouseover', function () {
holder.innerHTML = '';
// We can't set innerHTML to images[_i]
// because it's a DomNode not a string
holder.appendChild(images[_i]);
});
})(i);
}
holder.addEventListener('mouseout', function (e) {
if (e.target !== holder)
return;
holder.innerHTML = '';
// Again, use var j = 0 . . .
for (var j = 0; j < images.length; j++) {
holder.appendChild(images[j]);
}
});
}
popImage();
.img {
width: 100px;
height: 100px;
background: #bada55;
border: 2px solid #333;
display: inline-block;
}
#holder {
position: relative;
width: 100%;// So doesn't collape and trigger mouseout
height: 100px;
background: red;
padding: 20px 0;
}
<div id="holder">
<!-- Again, these would be your image tags -->
<div class="img">1</div>
<div class="img">2</div>
<div class="img">3</div>
<div class="img">4</div>
</div>
I had 10 mins before leaving work so I had a crack at this to see how I would do it and give you some ideas.
Here is my implementation (https://jsfiddle.net/hg7s1pyh/)
I guess the main thing here is that I've broken it down into lots of smaller parts, this makes solving problems far easier, each method is concerned with doing one thing only.
You will also note the use of classes to show and hide content rather than removing it entirely, this takes lots of the arduous work out of this feature.
function attachEvents() {
var images = getImages();
images.forEach(function(image) {
attachMouseOverEvent(image);
attachMouseLeaveEvent(image);
});
}
function attachMouseOverEvent(element) {
element.addEventListener('mouseover', function(e) {
var clonedImage = e.target.cloneNode();
addImageToPreview(clonedImage);
});
}
function attachMouseLeaveEvent(element) {
element.addEventListener('mouseleave', function(e) {
removeImageFromPreview();
});
}
function getImages() {
return document.querySelectorAll('.js-image');
}
function getImagePreviewElement() {
return document.querySelector('.js-image-box');
}
function addImageToPreview(imageElement) {
var previewElement = getImagePreviewElement();
previewElement.classList.add('previewing');
previewElement.appendChild(imageElement);
}
function removeImageFromPreview() {
var previewElement = getImagePreviewElement();
previewElement.classList.remove('previewing');
var image = previewElement.querySelector('.js-image');
image.remove();
}
attachEvents();
.image-box {
position: relative;
min-height: 400px;
width: 400px;
border: 1px solid #000;
text-align: center;
}
.image-box .placeholder {
position: absolute;
top: 50%;
text-align: center;
transform: translateY(-50%);
width: 100%;
}
.image-box.previewing .placeholder {
display: none;
}
.image-box .image {
position: absolute;
top: 50%;
text-align: center;
transform: translate(-50%, -50%);
height: 100%;
width: 100%;
}
.images {
margin-top: 10px;
}
<div class="js-image-box image-box">
<div class="placeholder">
Placeholder
</div>
</div>
<div class="images">
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/150x150"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/400x400"></div>
<div class="col-md-3 img-fluid"><img class="js-image image" src="http://placehold.it/350x150"></div>
</div>
Can anyone explain how to make a user list like as shown in the image below...
I'm making a project in Meteor and using Materialize for template and I want to display the list of assigned users. If there are more than a particular count(say 5) of users i want them to be displayed like on that image... I have tried googling this and haven't found anything useful. I also checked the Materialize website and found nothing useful. So if anyone has an idea please help share it.
Ok so this is the output html, in this case i only have one member but in real case I will have more:
<div class="row"> ==$0
<label class="active members_padding_card_view">Members</label>
<div class="toolBarUsers flex" style="float:right;">
<dic class="other-profile" style="background-color:#f06292;">
<span>B</span>
</div>
This is the .js code
Template.profile.helpers({
randomInitials: function () {
var chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
var nLetter = chars.charAt(Math.floor(Math.random()*chars.length));
var sLetter = chars.charAt(Math.floor(Math.random()*chars.length));
return nLetter + sLetter;
},
tagColor: function () {
var colors = ["#e57373","#f06292","#ba68c8","#9575cd","#7986cb","#64b5f6","#4fc3f7","#4dd0e1","#4db6ac","#81c784","#aed581","#dce775","#fff176","#ffd54f","#ffb74d","#ff8a65","#a1887f","#e0e0e0","#90a4ae"];
return colors[Math.floor(Math.random()*colors.length)];
},
randomAllowed : function(possible) {
var count = Math.floor((Math.random() * possible) + 1);
if(count == 1) {
return;
}
return "none";
},
membersList() {
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
return CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId});
},
memberData: function() {
// We use this helper inside the {{#each posts}} loop, so the context
// will be a post object. Thus, we can use this.xxxx from above memberList
return Meteor.users.findOne(this.lkp_user_fkeyi_ref);
},
showMembers() {
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
let membersCount = CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId}).count();
////console.log(membersCount);
if (membersCount > 0) {
$('.modal-trigger').leanModal();
return true;
} else {
return false;
}
},
});
Right now if I add a lot of users I get this:
This can be done in many ways, but I've used CSS Flexbox.
I've used two <div>s one contains single user circles having class .each-user that is expanding (for reference I've taken 6) and another contains the total number of users having class .total-users.
It's a bit confusing but if you look into my code below or see this Codepen you'll get to know everything.
html, body {
width: 100%;
height: 100%;
margin: 0;
font-family: Roboto;
}
.container {
display: flex;
align-content: center;
justify-content: center;
margin-top: 20px;
}
/* Contains all the circles */
.users-holder {
display: flex;
}
/* Contains all circles (those without total value written on it) */
.each-user {
display: flex;
flex-wrap: wrap;
padding: 0 10px;
max-width: 300px;
height: 50px;
overflow: hidden;
}
/* Circle Styling */
.circle {
width: 50px;
height: 50px;
border-radius: 50%;
margin-right: 10px;
}
.each-user .circle {
background: #00BCD4;
}
.each-user .circle:last-child {
margin-right: 0;
}
/* Circle showing total */
.total-users {
padding: 0;
margin-bottom:
}
.total-users .circle {
background: #3F51B5;
margin: 0;
position: relative;
}
.total-users .circle .txt {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
}
<div class="container">
<div class="users-holder">
<div class="total-users">
<div class="circle">
<span class="txt">+12</span>
</div>
</div>
<div class="each-user">
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<div class="circle user-circle"></div>
<!-- Sixth Circle -->
<div class="circle"></div>
</div>
</div>
</div>
Hope this helps!
I've used jQuery. See this https://jsfiddle.net/q86x7mjh/26/
HTML:
<div class="user-list-container">
<div class="total-circle hidden"><span></span></div>
<div class="user-circle"><span>T</span></div>
<div class="user-circle"><span>C</span></div>
<div class="user-circle"><span>U</span></div>
<div class="user-circle"><span>M</span></div>
<div class="user-circle"><span>R</span></div>
<div class="user-circle"><span>Z</span></div>
<div class="user-circle"><span>N</span></div>
<div class="user-circle"><span>O</span></div>
<div class="user-circle"><span>M</span></div>
<div>
jQuery:
var items_to_show = 5;
if($('.user-circle').length > items_to_show){
var hide = $('.user-circle').length - items_to_show;
for(var i = 0; i < hide; i++){
$('.user-circle').eq(i).addClass('hidden');
}
$('.total-circle').removeClass('hidden');
$('.total-circle span').text('+' + hide);
}
So after quite some time I have solved the problem. I am posting my answer here for anyone that will in the future experience a similar issue...
Have a good day!
I have added the following lines of code to my template:
return CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId},{sort: {createdAt: -1}, limit: 3});
diffMembers(){
const instance = Template.instance();
const cardDataId = new Mongo.ObjectID(instance.data.cardData._id.valueOf());
const limit = 3;
const allMembersOnCard = CardDataMembers.find({lkp_card_data_fkeyi_ref: cardDataId}).count();
let remainingMembers = allMembersOnCard - limit;
return remainingMembers;
},
And in the HTML included:
<div class="other-profile" style="background-color:#dedede;">
<span>+{{diffMembers}}</span>
</div>