I have a webpage made with svelte and I am trying to create a sort of tooltip for a div that should be displayed on hover.
So I wrote this:
<div
class="test"
style="background-color: {testcase.failure ? 'red' : 'green'}"
on:mouseenter={() => (testcase.mouseover = true)}
on:mouseleave={() => (testcase.mouseover = false)}>
{#if (testcase.failure && testcase.mouseover)}
<div class="failure">
<div class="name">{testcase.class}:{testcase.name}</div>
<div class="error">{testcase.failure}</div>
</div>
{/if}
</div>
and it works in the sense that the failure div is shown only on hover.
However it is of course shown underneath all elements that are "after" it in the DOM.
So I added a z-index to failure, but it does nothing. It is still displayed underneath everything despite having a much higher z-index.
After experimenting a bit I figured out that if I remove the if statement and simple always display the failure div, then it is "properly" displayed on top of everything else.
This seems really weird for me and I cannot figure out why it does this. Does anyone have any good explanation for it?
(I know that I can probably always display it and just change opacity or something to get the same effect, but I am curious about the root cause)
Related
Put very briefly
I wrote a vuejs component that includes a small icon that displays some text when being hovered over.
I created a minimal example here: https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136
The special thing is: when the icon is at the very bottom of the page the text is displayed above the icon instead of below such that the page does not have to be extended.
However, even when the text is displayed above the icon, the page is extended without any content (even the html ends before the whitespace).
I tested this in Firefox and Chromium.
I would like to understand where this white-space comes from and how to get rid of it.
I will explain in more detail what I'm trying to do below.
If my entire approach to this does not make much sense I'm also happy for suggestions.
My approach so far
First step: display text when hovering an image
What I want to achieve seems quite straight forward with vuejs at first glance.
I want to have a vue component that shows an image (for example a small info icon).
When one hovers over the image with the mouse a text is displayed that overlays the other content on the page.
This can be achieved in different ways - for example:
Create a component that has a hovered variable like so
<script>
export default {
name: 'ShowBox',
props: ['showText'],
data() {
return {
hovered: false,
}
},
}
</script>
In the template, bind the class of the to be shown text to the hovered variable and handle an #mouseenter and #mouseleave event in the image accordingly like so
<template>
<div class="show-box">
<div class="show-icon">
<img width="50px" #mouseenter="hovered = true" #mouseleave="hovered = false" src="...">
</div>
<div class="show-text" v-bind:class="{'hide': !hovered}">
{{ showText }}
</div>
</div>
</template>
Finally, in the style the class show-box should be positioned and the class show-text should include a position: absolute such that it overlays the text.
The class hide should somehow hide the text - for example with opacity: 0.
So far so good - for sure one can add a lot of stuff to make this more decent but for the sake of the example let's keep it simple.
The result might look as
this (link to picture).
Second step: display image above the icon when at the bottom of the page
Let's imagine the icon is at the very bottom of the page.
If one then hovers the icon the page might have to be extended in order to display the full text.
I want to avoid this and have the text displayed above the icon instead.
For this I took the following steps:
I wrapped the text in an extra div.
The text is now not rendered anymore when the page loads but only when one hovers over the image though a display: none in the style.
<template>
<div class="show-box">
<div class="show-icon">
<img width="50px" #mouseenter="hovered = true, , textRendered = true" #mouseleave="hovered = false" src="...">
</div>
<div class="show-text-wrap">
<div class="show-text" v-bind:class="{'not-rendered': !textRendered, 'hide': !hovered, 'render-top': renderTop}">
{{ showText }}
</div>
</div>
</div>
</template>
Furthermore, I included a life-cycle hook mounted such that after the page is rendered it is checked, whether there is enough space below the show-text-wrap div to display the text.
If not, a variable renderTop = true is set.
The style of the show-text div is now also bound to the renderTop variable such that if it is set (in the case of insufficient space at the botton of the page) the image is rendered above the icon (also see template above).
The mounted hook looks like
mounted() {
this.$nextTick(function () { //to be sure that page is fully rendered
var el = this.$el.lastElementChild; //show-text-wrap element
var rect = el.getBoundingClientRect();
if (rect.bottom + window.pageYOffset + 300 > (document.body.clientHeight)) {
this.renderTop = true;
}
}
)
},
Finally, the render-top class simply translates the text to the top.
.render-top{
transform: translateY(-100%);
}
The result when hovering over an icon at the bottom of the page might look like
this (link to picture).
The problem
This works fine in principle.
The only problem is: when the text is moved to the top as intended, some extra unwanted white-space appears at the bottom of the page (tested in Firefox and Chromium).
The white-space interestingly extends the html document.
I don't understand where this comes from and want to get rid of this since the whole point of moving the displayed text to the top was to avoid the page being extended.
In
this (link to image)
one can see the extra white-space that extends beyond the html document.
I also tried to move the text above the icon by different means for example
.render-top{
bottom: 0;
}
or even with hard coding pixel numbers like
.render-top{
bottom: 100px;
}
but none of these solve the problem.
I created a working minimal example for this https://codesandbox.io/s/sweet-jackson-v0kgc?file=/src/components/ShowBox.vue:115-136
where the problem can also be seen.
Any help is greatly appreciated - many thanks in advance!
A relative positioned element behaves as if it hadn't been moved
By now I found out the answer myself.
I will post it in case someone finds this useful.
The goal was to always move the text-box inside the viewport.
However, in the style the show-box's position was defined as position: relative; and only its direct parent show-text-wrap is absolutely positioned.
According to https://developer.mozilla.org/en-US/docs/Web/CSS/position a relative positioned element is offset relative to its original position.
This will not change the position of any other elements in the document.
This means that the layout of the document will behave as if the element was still at its original position.
Since the <show-box> originally reached beyond the <html> element such that the page was extended this is still the case regardless of the fact that the element appears moved into the document.
So in order to fix the problem change
.show-text {
position: relative;
}
to
.show-text {
position: absolute;
}
I'm writing a single page web app with vue. It has 4 "page.vue"'s and in either of those goes a right and left child .vue component.
for example Page1.vue looks like this (I left style and script to keep it short)
Page1Links & Page1Rechts are the child VUEs that I try to load into the main part
(2 seperate componets were used because one needs to be changed for a more complex one soon and it's written so that the other component can stay as is)
<template>
<div>
<div class="page-wrapper">
<div class="page-container">
<div class="page-header">
<h1 id="page-h1"><span>Mauderer</span> Containertreppenkonfigurator</h1>
</div>
<div class="page-main-content">
<Page1Links id="links"/>
<Page1Rechts id="rechts"/>
</div>
<div class="page-footer">
</div>
</div>
</div>
</div>
</template>
WHAT I WANNA SEE is a header component spanning the whole page width
below that the 2 child components next to each other spanning full width
(70% left comp 30% right comp)
(wrapped in some nice design to make it clear they belong together for e.g. I tried a box shadow arround the page-main-content class, which is not showing another indicator that how I tried it won't work, or rather the main div is simply empty)
and below that the footer full width
WHAT I GET is all divs below each other at the top of the page:
1. header div
2. main div(which appears empty)
3. footer div
and below that the two child.vue, they are next to each other but that is only because I forced them too inside them.
EDIT:
Ok thanks for the info with flex you two that is helpfull in pulling the layout out of the child components.
To clarify:
My problem is that my child components are not rendered inside the main div where their tag is.
That div class="page-main-content" is empty and the Page1Links and Page1Rechts are rendered below the footer.
And I thought from the little vue experience that I have, a .vue component is rendered to where you put it's tag but that does not work here, and I don't know why.
problem example
In this pic you can see the problem:
the page parent vue and all its divs are rendered in the top(2nd grey bar is the footer) and even though I expected my approach to render the children into that main part they are actually rendered after the page 1 so basically below it.
I have bootstrapvue included I just thought it would be simpler this way, if I'm wrong please tell me
But where is your css code?
I do not know if i understand it right, but if you want to #links and #rechts next to each other you can make it by flex for example.
.page-main-content{display: flex;}
#links{flex: 0 0 70%;}
#rechts{flex: 0 0 30%;}
I tried a couple of times before it worked and it actually solved it, I don't really understand why though? The flex now pushed the children inside the page1 into the page-main-content.
I do understand that this is way more handy than what I did (code the size of the component into itself) but the components were next to each ohter before so what the flex did is force the children to render inside the parent instead of below/after it.
So thanks, problem solved.
If sbd. knows a short answer, I'm still interested into why that is though.
When the Vue animation ends the content "jumps" back up as you can see in this example:
https://codepen.io/propsoft/pen/PRYjBZ
<transition name="slide-y-transition">
<div v-show="compare">
Here is the hidden {{ content }}
</div>
</transition name="slide-y-transition">
Any ideas on how to fix this issue?
Cheers!
The problem with the animation is that it uses transform: translateY(-15px);, which causes only a visual translation of the content but the div is still occupying the same space. One way to fix that is to animate other properties, like height for example. The drawback with that is that the div needs to have a fixed height, which may not be possible in all situations.
You can test different approaches without animating anything, see what causes the div below to actually be displaced or not. And then you can animate that.
Check out this codepen with your example animated using height: https://codepen.io/noeldemartin/pen/ommxZG
I'm trying really hard to replicate what happens here angular theme on Wordpress.
I've been able to get isotope to filter the post_thumbnails display them and it animate great but what I'm stuck on is when clicking an image or link the content of that post/portfolio gets displayed in a new div. Ideally in place and pushing boxes out the way so if you're on a mobile you don't have to scroll to the top.
Any pointers to get me started would be great, just can't find anything like this anywhere and think it would be very useful to others :)
Thanks
Actually that can be achieved quite easily. Basically you'll merely have to add a click handler to all Isotope items. The handler has to figure out which element has been clicked (e.g. by checking class names of the clicked item, but of course there are numerous ways) and then add the respective content to your div element.
If the content has to be shown in place, it's even easier. You can simply add the preview and the full content to the same Isotope item, but hide the full content by default:
<div class="item">
<div class="preview">...</div>
<div class="full">...</div> <!-- hidden with CSS -->
</div>
Then add a click handler to all Isotope items:
$(".item").click(function(){
$(this).toggleClass("big");
$("#container").isotope("reLayout");
});
By calling .isotope("reLayout") the other items are pushed out of the way when the clicked one expands.
Finally you need some basic CSS rules making div elements with .big bigger, hiding .full by default, but showing it when .big is set in the parent div. In that case .preview has to be hidden of course; this can all be done with CSS, no JavaScript/jQuery required.
Ok, it's a bit cumbersome to explain - I guess an example says more than a thousand words: JSFiddle
Of course that's just a very basic example, but hopefully it explains what I meant. ;)
I have two div elements, box_1 and box_2:
<div id="box_1" style="position:absolute;top:0;left:0;width:500px;height:500px;border:1px solid gray;z-index:2;">
</div>
<div id="box_2" onclick="alert(1);" style="position:absolute;top:0;left:0;width:300px;height:300px;background-color:red;z-index:1;">
how to activate me?(do not inner #box_1,and z-index less than box_1)
</div>
Check it >>> demo
How to activate box_2 ? (do not inner #box_1,and z-index less than box_1)
activate == show alert(1) when click box_2
I don't fully understand your question. The way you've got those boxes styled, the only way to get that <div> to be clickable is to give it a "z-index" value larger than 2.
Alternatively, you could give the other <div> a "z-index" value less than 1, or hide it.
If you can't change the markup, then the only thing you can do is catch events on the top <div> and forward them to the covered-up <div>. That's pretty easy with jQuery — just add handlers to the top <div> and use ".trigger" to forward the events.
edit — like this maybe:
$('#box_1').click(function() {
$('#box_2').trigger('click');
return false;
});
Now that's going to catch events from all over the top <div>. You could check the event mouse coordinates to see whether they're inside the bottom <div> before triggering the event.
You've done the right thing except box_1 is transparent so box_2 is showing through. If you give box_1 a background color like blue you'll see it appears on the top.
<div style=";width:500px;height:500px;border:1px solid gray;z-index:2;background:blue;" id="box_1">
</div>
<div style="width:300px;height:300px;background-color:red;z-index:1;" id="box_2" onclick="alert(1);">
how to activate me?(do not inner #box_1,and z-index less than box_1)
</div>