So I have this button which, when clicked upon, should move 480px to the right and change icon. I am using Angular JS and the changing of the icon works, but the button does not move.
This my is my HTML:
<div id="menuButton" ng-click="toggleMenu()">
<div class="hamburgerMenuButton" ng-class="menu.shown ? 'menuOpen' : 'menuClosed'">
</div>
</div>
CSS:
#menuButton {
width:52px;
height: 50px;
padding: 10px;
z-index: 1000;
position: relative;
}
#menuButton:hover {
background-color: #02CDED;
border-color: #FFFFFF;
}
.hamburgerMenuButton {
width: 32px;
height: 30px;
}
.menuOpen {
left: 480px;
background-image: url('images/arrow.png');
}
.menuClosed {
background-image: url('images/hamburger.png');
}
When I look in the developers view in Chrome I can see that the class changes on the hamburgerMenuButton div, and the property left: 480px; is added, but it just does not move. The icon does change as expected.
Add position:relative;, or position:inherit;, to the .hamburgerMenuButton.
It's not moving because it has position set to static, per default. The left does not mean anything to the element.
Though you've set position:relative; on the parent div, it just so happens that position is not an inherited property;
Related
Background
I have an HTML div which contains a ‘tooltip’-like feature (i.e., a text box pops up when a certain element is clicked or hovered over); this tooltip has decorative pseudo-elements to make it look like a ‘speech bubble,’ added in css as :before and :after .
I have a JS script, which is intended to show and hide the tooltip and decoration, in response to click events (i.e., toggle them between ‘show’ and ‘hide’ states).
Problem
I can’t get the decorative pseudo-elements to hide when the tooltip is hidden; as pseudo-elements, they are not part of the DOM and so I can’t use normal selectors to manipulate them.
When the tooltip is hidden on click, the decorative pseudo-elements persist, which is not a usable result.
I can’t do away with the decorative elements, they are part of the work specification.
Approach tried so far
Based on this question, my thought was to add an empty span with its own class, to which I’d prepend and append these pseudo-elements. Then, add or remove the class on click based on whether it exists already, or not.
I have also tried setting the class to which the pseudo-elements are pre/appended to display:none on click, but this also seems not to work
However, I cannot convince the pseudo-elements to hide on click.
I’ve included a screenshot of what these remnant pseudo-elements look like in the live environment.
Note: I tried to work up a running simulation for the purpose of this question, but I wasn’t able to and the original css file is massive; the code included below is for reference only.
All guidance is much appreciated!
const barContainer = document.querySelector(".bar-container");
const decorationElement = document.querySelector("#decoration");
document.addEventListener('click', function(event) {
console.log('click event listener triggered');
if (event.target.closest('.link') || event.target.classList.contains('link')) {
if (barContainer.classList.contains('open')) {
barContainer.classList.remove('open')
decorationElement.classList.remove('decoration')
document.querySelector('.tooltip-container').setAttribute('style', 'display:none');
} else {
barContainer.classList.add('open')
decorationElement.classList.add('decoration')
document.querySelector('.tooltip-container').setAttribute('style', 'display:block');
}
} else {
barContainer.classList.remove('open')
decorationElement.classList.remove('decoration')
document.querySelector('.tooltip-container').setAttribute('style', 'display:none');
}
});
.foo-container {
height: auto;
position: relative;
}
.bar-container {
height: auto;
position: relative;
text-align: center;
}
.bar-container:hover .tooltip-container,
.tooltip-container:hover,
.bar-container.open .tooltip-container {
position: absolute;
display: block;
text-align: left;
background-color: #ffffff;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
bottom: 50px;
right: 5%;
border-radius: 4%;
font-weight: 300;
max-width: 90%;
font-size: 14px;
padding: 20px 0;
}
/*the below two rule sets create the rotated 'decoration' */
.bar-container:hover .tooltip-container:before,
.tooltip-container:hover:before,
.bar-container.open .tooltip-container:before,
.foo-container .bar-container:hover .decoration:before {
content: "";
width: 65px;
height: 35px;
position: absolute;
overflow: hidden;
transform: rotate(-180deg);
z-index: 10;
bottom: 0;
left: 170px;
background-color: white;
}
.foo-container .bar-container.open .decoration:before,
.foo-container .bar-container:hover .decoration:before {
content: "";
position: absolute;
width: 30px;
height: 30px;
background: #fff;
transform: rotate(45deg);
left: 30px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
z-index: 2;
top: -42px;
}
/* end 'deocration' */
<div class="foo-container">
<div class="bar-container">
<p>text <span class='link'>the-link<span id='decoration' class='decoration'></span></span>
</p>
<div class='tooltip-container'>
<p>lorem </p>
</div>
</div>
</div>
Screenshot of the undesirable 'persistent pseudo-elements' behavior -->
On my page, I'm displaying a log file in a div element with overflow-y:auto. In the top right corner of the div, I'm overlaying a close button div with position:relative.
When the scrollbar appears, the button is overlaying the scrollbar which is hard to see and looks ugly. You can see an example here: https://jsfiddle.net/4azw0rLf/
Moving it with javascript when scrollHeight exceeds clientHeight feels like a hack. Is there an option in CSS to move the close button to the left for the width of the scrollbar as soon as it appears?
You can wrap your terminal and move your close button inside. I created a minimal example starting from your code.
EDIT
With the first answer the close button scrolled along with the text, I corrected using the position: sticky; and top:0px;
It is not compatible with all browsers, but with most, you can check compatibility here.
const terminal = document.getElementById("terminal");
addText = () => {
terminal.innerHTML += "overflowing line<br>";
}
#container-terminal {
position: relative;
overflow-y: auto;
border: 2px solid;
height: 100px;
width: 200px;
}
#terminal {
position: absolute;
height: 100%;
width: 100%;
}
#closeBtn {
background-color: red;
position: -webkit-sticky;
position: sticky;
top:0px;
width: 20px;
display: inline-block;
float: right;
}
<div onclick="addText()" style="cursor:pointer;">Click to add text</div><br>
<div id="container-terminal">
<div id="terminal">CSS only<br>CSS only<br>CSS only<br>CSS only<br>CSS only<br></div>
<div id="closeBtn">X</div>
</div>
I'm trying to make specified div's on my page interactive and thus, change focus (come to the foreground if you will) when clicked. Essentially, I would like the div's to act much like the windows do on a computer running Microsoft Windows.
This is what I've come up with so far using z-index and it does work, sort of. The problem is that the div's appear to have their own "order" so to speak when it comes to overlapping each other based on where they are placed in the HTML.
For instance, if you click "Div2" then "Div1", you can see that "Div2" ends up actually going back behind "Div3" rather than staying in front of it as it previously was. I'd like order to be retained as clicked. If you click Div2, it should be in the front, then you click Div1 and it would then be in front of Div2, etc.
Any ideas on a way around this would be greatly appreciated. Thank you in advance!
// Global vars
var lastFocused;
// When left mousedown on a ".window" element, remove the ".window-focus" class from id
// specified in the global var "lastFocused". Next, update the global var "lastFocused"
// with the most recently clicked element id. Lastly, add the ".window-focus" class to
// the id specified in global var "lastFocused".
$('.window').mousedown(function () {
$(lastFocused).removeClass('window-focus');
lastFocused = "#" + $(this).attr("id");
$(lastFocused).addClass('window-focus');
});
// Make all ".window" elements resizable and draggable.
$('.window').draggable({containment: '#container'}).resizable({containment: '#container'});
html, body, #container {
height: 100%;
width: 100%;
font-family: Roboto;
background-color: #333;
color: #c9c9c9;
font-size: 1em;
overflow: hidden;
}
.window {
position: absolute !important;
border: 1px solid #999;
background-color: #222;
height: 100px;
width: 100px;
z-index: 1;
}
#div1 { top: 20px; left: 20px; }
#div2 { top: 60px; left: 60px; }
#div3 { top: 100px; left: 100px; }
.window-focus { z-index: 2; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="window" id="div1">ONE</div>
<div class="window" id="div2">TWO</div>
<div class="window" id="div3">THREE</div>
Your issue is that you are always giving the the currently clicked div a z-index of 2, which does bring it to the front, but then when you remove that class from an element it simply goes back to it's original z-index. You need that last-applied z-index not to change and have the next-clicked element get a z-index that is one higher than the previous one.
Just keep track of the last z-index assigned to the last-clicked div element and increment it by one after each assignment so that the next clicked element will get a higher z-index assigned to it.
NOTES:
There is no need for the lastFocused variable, the .window-focus
CSS selector, or to assign a z-index:1 to the .window elements.
You were exactly right about elements having an implicit z-index
based on where they are in the HTML. For sibling elements, the z-index is simply based on the sequence. The earlier in the sequence, the lower the z-index. But, it's more complex when you start working with elements that don't share the same parent. See the stacking context for details.
JQuery recommends using the element.on("eventName", callback)
method rather than event-specific methods (i.e.
element.mousedown(callback)).
It's probably not a good idea to use a class name of .window for
elements that are not the window object. It will cause confusion. For something like this, a class name of stackable or draggable seems appropriate.
var highestZ = 3; // There are 3 divs, so highest z-index in use is initially 3
// When any of the div.stackable elements get clicked...
$('.stackable').on("mousedown", function () {
$(this).css('z-index', ++highestZ); // Clicked div gets a z-index one higher than prevous highest
});
// Make all ".stackable" elements resizable and draggable.
$('.stackable').draggable({containment: '#container'}).resizable({containment: '#container'});
html, body, #container {
height: 100%;
width: 100%;
font-family: Roboto;
background-color: #333;
color: #c9c9c9;
font-size: 1em;
overflow: hidden;
}
.stackable {
position: absolute !important;
border: 1px solid #999;
background-color: #222;
height: 100px;
width: 100px;
user-select:none;
cursor:pointer;
}
#div1 { top: 20px; left: 20px; }
#div2 { top: 60px; left: 60px; }
#div3 { top: 100px; left: 100px; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://code.jquery.com/ui/1.12.1/jquery-ui.min.js"></script>
<div class="stackable" id="div1">ONE</div>
<div class="stackable" id="div2">TWO</div>
<div class="stackable" id="div3">THREE</div>
I added code to make a div, #pending-friend-list-dropdown, close when clicking outside of it. This works fine, but now when clicking on my image div, friend-icon, the drop-down div will not close now.
As you can see in my snippet, the image div is what opens the drop-down box. I am just trying to figure out how that image div can be used to open and close the drop-down, while using the mouseup function to close the drop-down div as well.
//Hiding Pending Friend Drop-down when clicking out of it
$(document).mouseup(function (e)
{
var container = $("#pending-friend-list-dropdown");
var friend_icon = $("#friend-icon");
if (!container.is(e.target) // if the target of the click isn't the container...
&& container.has(e.target).length === 0) // ... nor a descendant of the container
{
container.hide();
}
else if (friend_icon.has(e.target)) {
container.hide();
}
});
//Toggle Pending Friend List
$("#friend-icon").click(function() {
$('#pending-friend-list-dropdown').toggle(100);
});
#main-bar {
width: 85%;
height: 60px;
position: relative;
margin-left: 15%;
background: red;
padding: 3px 0;
}
#main-bar-container {
border: 1px solid black;
margin: 0 10px;
position: relative;
width: 95%;
height: 56px;
left: 2%;
}
/*---- Pending Friends List----*/
#friend-icon {
display: inline-block;
cursor: pointer;
position: absolute;
right: 20%;
top: 15px;
}
#friend-icon img {
height: 30px;
width: 30px;
}
#pending-friend-list-dropdown {
height: 500px;
width: 400px;
overflow: scroll;
z-index: 100000;
position: absolute;
left: 70%;
top: 70px;
background: blue;
display: none;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="main-bar">
<div id="main-bar-container">
<div id="friend-icon"><img src="../icons/collection/social.png" alt="Pending Friends"></div>
</div>
</div>
<div id="pending-friend-list-dropdown">
</div>
You can achieve this more simply by running the code whenever someone clicks on the html-element (the entire page).
Then check if the click is located on certain elements.
There is also no need to give the instructions in two places for what to be done when clicking on "#friend-icon". I have removed the second instance of this in the below code, and just moved the .toggle up to the if statement.
It now works like a charm:
$("html").click(function(event)
{
var container = "#pending-friend-list-dropdown";
var friend_icon = '#friend-icon, #friend-icon img';
if ( $(event.target).is(friend_icon) ) // clicking on the toggler-div or the img it contains
{
$(container).toggle(100);
}
else if (!$(event.target).is(friend_icon) // clicking outside of the toggler
&& !$(event.target).is(container)) // and outside of the toggled div itself
{
$(container).hide();
}
});
Here's a jsfiddle: https://jsfiddle.net/r54ardcz/2/
I'll give a third option just so that all the ones I know are on this site. This is the option that Office Fabric UI uses (https://dev.office.com/fabric#/components/contextualmenu) where I think #zheer-mchammer-husain's answer is more along the Twitter Bootstrap model.
Essentially you create a layer over your whole page (height: 100vh and width: 100vw; position: fixed) and then put your dropdown content inside that layer. When the user clicks that layer, it closes the whole layer at once disappears and all is done.
I'm coding an image overlay w/ jQuery, and I got it working (somewhat). If you hover over the first image, it successfully appears; however, if you hover over the second one, it doesn't even work. I don't even know what the problem is! I think it has to do with unique IDs or whatever. I tried classes, and it didn't work.
Here is the fiddle :: http://jsfiddle.net/PFWcz/7/
$(document).ready(function () {
$('.overlay-link').mouseover(function () {
$(this).find('.overlay').fadeIn(200);
}).mouseleave(function () {
$(this).find('.overlay').fadeOut(200);
});
});
There are a few issues. As esqew pointed out, you're using the same IDs, which must be unique.
Addressing that, you'll still see the "same" overlay in your fiddle (http://jsfiddle.net/PFWcz/7/), but it's actually not - you're just now seeing a positioning issue.
Take a look at this fiddle:
http://jsfiddle.net/PFWcz/10/
You'll notice that when you hover over the first image, the red overlay is "1", and when you hover over the second image, the overlay is "2".
Previously (with the "helloooooo" text), the red overlays appeared the same (because of the content and positioning)...
Address the ID and position issues, and it should work.
Here's a fiddle demonstrating fixed position and ID:
http://jsfiddle.net/PFWcz/16/
The main changes is giving the container (<div>) positioning:
div {
float: left;
margin: 30px;
position: relative;
}
Also, I removed offsets (left, top) and floats, applying those to the parent container. A quick, simple fix.
You need to make your overlay-link elements your containers from which child elements inherit positions.
<a class="overlay-link">
<img src="https://d13yacurqjgara.cloudfront.net/users/67256/screenshots/1191507/shooot.png"/>
<span class="overlay"><i>hellllllllooooooo</i></span>
</a>
Your overlay-link class needs to have position: relative and will define the position and size of it and its children:
.overlay-link {
position: relative;
display: block;
width: 292px;
height: 219px;
margin: 30px;
}
Any child inside needs to have position: absolute and its width and height set to 100% of the container:
img {
position: absolute;
border-radius: 2px;
width: 100%;
height: 100%;
top: 0;
left: 0;
}
.overlay {
background-color: rgba(223, 71, 71,0.70);
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
border-radius: 2px;
display: none;
text-align:center;
}
Now when you hover over an element, it will create the overlay over that element and not the other one as you were experiencing earlier.
Jsfiddle: http://jsfiddle.net/PFWcz/14/
You're using the same id, which must be unique. Use the class attribute.
As some of the answered already said there is issue with the id's, I don't want to repeat. Since you have a multiple place where you want to show some text on rollover, using class would be a better solution/way to go ahead with.
Here is the change I did in the fiddle:
.overlay-link { /*This class is added. Since an absolute positioned element places itself relative to its parent who is either a relative positioned element or an absolute positioned element. I made the parent of the .overlay div relative.*/
position: relative;
background-color: #ff0;
}
.overlay {
position: absolute;
background-color: rgba(223, 71, 71,0.70);
left: -322px; /*Positioning the .overlay element based on its parents left*/
width: 292px;
height: 219px;
border-radius: 2px;
top: 30px;
display: none;
text-align:center;
}
.overlay i { /*There is no .shot element in the DOM. I replaced it by .overlay*/
background-color: #df4747;
border-radius: 999px;
padding: 10px;
width: 60px;
height: 60px;
color: #fff;
font-size: 30px;
top: 80px;
left: 116px;
position: absolute;
display: block;
}
This is based on my understanding. Let me know if it works.
Here, this is what you want
Fiddle: http://jsfiddle.net/D33Yk/
$(window).on('load',function () {
$('.overlay-link').mouseover(function(){
var overlay = $(this).children('.overlay');
var img = $(this).children('img');
$(overlay).css('left',$(img).offset().left);
$(overlay).css('top',$(img).offset().top);
$(overlay).css('width',$(img).width());
$(overlay).css('height',$(img).height());
$(overlay).fadeIn(200);
}).mouseleave(function(){
$(this).children('.overlay').fadeOut(200);
});
});
Because you had the overlay positioned absolutely in CSS, both overlays always covered the first image. I now set the left, top, width and height in JS, so the overlays cover their respective image.
I also changed this in CSS:
.overlay {
display: none;
position: absolute;
background-color: rgba(223, 71, 71,0.70);
border-radius: 2px;
text-align:center;
}
removed the top, left, width, height
...and this in HTML (I changed both, but I only show one since they are identical):
<div>
<a class="overlay-link">
<img src="https://d13yacurqjgara.cloudfront.net/users/67256/screenshots/1191507/shooot.png"/>
<span class="overlay"><i>hellllllllooooooo</i></span>
</a>
</div>
changed all the id's to classes, and removed id where it was not necessary