css issue: absolute positioned span would not extend out of containing div - javascript

as you can see in the following image, i have a simple form that validates the fields in real time and generates a span that says whether that field is valid or not. problem is, for some reason that span would not extend out of the containing div even though its has position:absolute in its css rule. by the way, the span gets its right position using javascript, that calculates the width of that field (since every field differs in width). any suggestions?
here is the css code:
div.row {
background:url('/img/formBackground.gif') repeat;
margin:0 2px 2px 2px;border-radius:10px 0 10px 0;
position:relative
}
.row span.valid,
.row span.invalid {
line-height:18px;
height:20px;padding:0 22px 0 5px;
display:block;font-size:13px;
border-radius:3px;position:absolute;
z-index:100;top:4px;right:160px
}
here is the html code:
<div class="row">
<label for="email">דואר אלקטרוני: <span class="required">*</span></label>
<div class="divider"></div>
<input type="text" name="email" id="email" style="width:250px" dir="ltr"/>
</div>
here is the javascript code:
email.onchange = function validateEmail() {
span = document.getElementById('span4');
span.style.right = calcDistance(email);
var reg = /^([A-Za-z0-9_\-\.])+\#([A-Za-z0-9_\-\.])+\.([A-Za-z]{2,4})$/;
el = email.value;
if(el == null || el == '') {span.setAttribute('class','invalid');span.innerHTML = "<span></span>עליך להזין דוא\"ל";return false;}
if(reg.test(el) == false) {span.setAttribute('class','invalid');span.innerHTML = "<span></span>כתובת הדוא\"ל אינה תקינה";return false;}
else {span.setAttribute('class','valid');span.innerHTML = "<span></span>תקין";return true;}
}
function calcDistance(el) {
var spanDistance = 160 + 20;
var targetWidth = el.offsetWidth;
return targetWidth + spanDistance + 'px';
}
This is how I want it to look (the span extends out of the containing div). I was able to get this result only by giving a certain width to the span, which is not a good solution because every message differs in it's width:

Elements with position:relative will "trap" any absolutely positioned child elements. In other words, the absolute element's coordinates are relative to the parent.
http://css-tricks.com/absolute-positioning-inside-relative-positioning/
http://www.quirksmode.org/css/position.html (see "The containing block" section)
While calculating the input widths to create your error message width is clever, this might just be too fragile of a design. You may end up with error messages that are simply too small for the message itself, especially when you consider that you don't truly have 100% control over the user's font size.
There's not enough code here to reproduce the output in your image, but possible solutions or workarounds may include:
Remove position:relative from div.row
Change your design, and move the error message underneath the input instead

After looking into some old posts, I managed to solve the problem by adding white-space:no-wrap; to the span CSS rule.

Related

Wrap content around Element on right, then Responsively above when exceeding min-width

I am trying to wrap text around an image that is positioned on the right and have the image shifted below the text when the screen becomes too small.
With these additional requirements:
The Text component has a minimum width for it's content next to the column (say 300px), which when exceeded pushes the image below the text.
The image component's size is unknown.
Images will be rendered at their native size, which may determine whether it is rendered to the right of the text or below it.
The same rules must apply when printing
Example
What have I tried?
https://codepen.io/CrocoDillon/pen/kmGCw (flawed as the image size is not pre-determined)
My current solution is a combination of various approaches. It's dirty/hacky as it uses JS to achieve part of it's goals which end up failing the print requirement.
It works as follows:
In html, render the IMAGE after the TEXT and floating the IMAGE to the right so that image is on right with text wrapped around it.
In JS, on window resize, if windowWidth - imageWidth > 300px (our min content width), update the style of the container to use flex-direction column-reverse so that the image appears below the text.
It fails the Print Requirement as the min-width check happens on window resize based on widths within window. Ideally it should now be based on the widths within the paper size/layout.
It is as follows:
HTML:
<div class="article-template-1">
<div class="article-content" ng-style="articleContentResponsiveStyle">
<div class="main-image-holder">
<div ng-repeat="imageLink in images">
<img ng-attr-src="{{imageLink.Location}}"
ng-attr-title="{{imageLink.Description}}" />
</div>
</div>
<span model="Article.Content" bind-html-compile>{{Article.Content}}</span>
</div>
</div>
CSS:
#newsArticle .article-content-holder .article-template-1 .article-content .main-image-holder {
float: right;
display: flex;
width: fit-content;
flex-direction: column;
}
#newsArticle .article-content-holder .article-template-1 .article-content .main-image-holder img {
max-width: 100%;
width: fit-content;
}
Controller:
var appWindow = angular.element(this.$window);
appWindow.bind("resize", this.updateArticleResponsiveStyle);
private exceedsMinContent:boolean = null;
private updateArticleResponsiveStyle = () => {
// The minimum size that inline content needs to be before images are repositioned
const minArticleWidth = 300;
let windowWidth = window.innerWidth;
let maxImageContainerWidth = windowWidth - minArticleWidth;
let imageContainerWidth = angular.element(document.getElementsByClassName('main-image-holder'))[0].getBoundingClientRect().width;
const previouslyExceedsMinContent = this.exceedsMinContent;
this.exceedsMinContent = imageContainerWidth > maxImageContainerWidth;
if (previouslyExceedsMinContent !== this.exceedsMinContent) {
this.$scope.articleContentResponsiveStyle =
this.exceedsMinContent ?
{
"display": "flex",
"flex-direction": "column-reverse"
} :
{
"display": "block"
}
this.$scope.$apply();
}
}
Yes, I find this incomplete solution hacky and would prefer a pure CSS approach if possible.

JS. Detect hidden child with parent overflow:hidden

I'm looking for simple way to detect, if child element of parent with overflow:hidden is visible within parent (it's not hidden by overflow).
I found something like this:
http://www.useallfive.com/thoughts/javascript-tool-detect-if-a-dom-element-is-truly-visible/
but i wonder maybe there is simpler solution.
Thanks in advance!
Assuming you want a vanilla js solution, try this:
function isVisible (parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetTop > parent.offsetHeight)
)
}
Basically "if the difference between the start of the parent element and the start of the child element is greater than the actual width or height of the parent, it's considered not visible"
Run the following snippet for an example:
var parent = document.getElementById('parent');
[].slice.call(document.querySelectorAll('.child')).forEach(function (child, i) {
console.log(i + ' is visible?', isVisible(parent, child));
});
function isVisible(parent, child) {
return !(
(child.offsetLeft - parent.offsetLeft > parent.offsetWidth) ||
(child.offsetTop - parent.offsetLeft > parent.offsetHeight)
)
}
* {
box-sizing: border-box;
}
#parent {
width: 200px;
height: 100px;
white-space: nowrap;
background: lightblue;
}
.child {
display: inline-block;
width: 75px;
height: 100%;
border: 1px solid green;
}
<div id="parent">
<div class="child">0</div>
<div class="child">1</div>
<div class="child">2</div>
<div class="child">3</div>
<div class="child">4</div>
</div>
You can do the following things:
check if the element has height and width that are bigger than 0px
check if the element display CSS property is not "none", preferably "block"
check if the element positioning is valid and if so, check if it´s "top" property is not bigger than the parent´s height
Same thing for left - if it is not bigger than parent´s width
When checking "width", check offsetWidth and clientWidth, those will show actual numbers as displayed to the client.
I had a similar requirement, but mine was a bit more complicated because the overflow: hidden element wasn't the first parent, it was like 5 or 6 elements away.
Just spend a whole day trying to do it with solutions from the internet(I've tried the repo you mentioned as well), but nothing worked.
So I've made this repo by myself (only JS, 2kb sized) https://github.com/LuizAsFight/is-element-visible.
It might help you, basically I just get the target element and climb the tree searching if any parent has a overflow:hidden, once I found it I get the parent's rect size, and check if the target element rect is inside the parent (visually, pixels)
for using it you just need to
import isVisible from 'is-element-visible';
const el = document.getElementById('id');
isVisible(el);
I hope it helps you, Best.

Created div elements' random margins not working

Problem and source code
I'm trying to create <div>s within another <div> at the click of a button. When the button is clicked, a new inner <div> is created (within the outer <div>) with a unique id. I have this part working but here's where I'm running into an issue: I want each inner <div> to have a random margin-top.
Javascript
function pressButton() {
number += 1;
makeDiv(number);
};
function makeDiv(x) {
var innerDiv = document.createElement("innerDiv" + x);
outer.appendChild(innerDiv);
innerDiv.setAttribute("style", "margin-top:" + Math.floor(Math.random()*51) + ";display:inline-block;width:48px;height:48px;background-color:#000;");
};
CSS:
#outer {
position:absolute;
white-space:nowrap;
height:118px;
overflow:auto;
width:100%;
padding:2px;
}
Result (after button is clicked 4 times)
<div id="outer">
<innerDiv1 style="margin-top:15;display:inline-block;width:48px;height:48px;background-color:#000;"></innerDiv1>
<innerDiv2 style="margin-top:23;display:inline-block;width:48px;height:48px;background-color:#000;"></innerDiv2>
<innerDiv3 style="margin-top:37;display:inline-block;width:48px;height:48px;background-color:#000;"></innerDiv3>
<innerDiv4 style="margin-top:0;display:inline-block;width:48px;height:48px;background-color:#000;"></innerDiv4>
</div>
The result (which I got from inspecting the inner elements in my browser) looks like everything worked - all the margin-tops are random like I wanted. However, the visual result is this:
As you can see, the black inner <div>s all have the same margin-top. What am I doing wrong? How can I make the created <div>s all have random margin-tops?
The CSS spec requires that a length (other than zero) that is missing a unit be treated as an error (and thus ignored). Therefore, add px to the end of your generated margin number, and all should be well.
Live Demo
Description
This happens, because you set the display:inline-block; property. This makes them all to be in one line, so they will allign to the innerDivx that has the highest margin-top.
Delete the display:inline-block; property and give them float:left;. If you want to keep the gap between them, also add margin-left:5px;. And don't forget that margin-top's value needs a unit. I think you wanted to use px.
Also <innerDivx> is not a valid HTML tag. You should change them to a <div> and use innerDivx as an id attribute. Also your tags use almost the same CSS styles so you should put the same ones to a class and add the class instead.
Full solution code
HTML
<button id="button1">Add box</button>
<div id="outer"></div>
JavaScript
var number = 0;
document.getElementById("button1").addEventListener("click", pressButton, false);
function pressButton() {
++number;
makeDiv(number);
};
function makeDiv(x) {
var innerDiv = document.createElement("div");
outer.appendChild(innerDiv);
innerDiv.className += " box";
innerDiv.setAttribute("id", "innerDiv" + x);
innerDiv.setAttribute("style", "margin-top:" + Math.floor(Math.random()*51) + "px;");
};
CSS
#outer {
position: absolute;
white-space: nowrap;
height: 118px;
overflow: auto;
width: 100%;
padding: 2px;
}
.box {
float: left;
width: 48px;
height: 48px;
background-color: #000;
margin-left: 5px;
}
This is likely caused by the position model used for inline-block elements - they're all being vertically-aligned at their bottom line in a row.
I suggest that you simplify this and use position: block with float: left
http://jsfiddle.net/2y5bJ/4/
I also suggest that you stick to standard elements to ensure cross-browser compatibility - don't create your own elements called innerDiv1 etc, but use div elements with unique IDs.
function makeDiv(x) {
var innerDiv = document.createElement("div");
outer.appendChild(div);
innerDiv.setAttribute('id', 'innerDiv' + x);
innerDiv.setAttribute("style", "margin-top:" + Math.floor(Math.random()*51) + "px;");
};
I think there is no tag available with name
<innerDiv1>
This may be the cause.

jQuery Blind Effect on Dragged Divs

I've got a number of divs on a page that I can drag and drop around. I also implemented the blind effect on them so that I can minimize and maximize the content if i don't want to see it.
I've got a problem that if I have 3 items stacked on top of each other, vertically, and I move the bottom one to the right of the top one and minimize the top div, everything slides upwards - and the 3rd div that I moved up slides right off the screen!
I've tried a ton of stuff, like making divs use absolute positioning but that causes problems of divs not sliding upwards in some circumstances. Reordering the divs dynamically causes the divs to be thrown around the screen because of offsets and relative positioning.
I just want it so that when the user drags divs over to the left or right and an "earlier" div is minimized, all subsequent divs don't get moved.
Any suggestions on this one are greatly appreciated.
Edit 1:
The problem I'm having with the absolute positioning is as follows.
I start with A, B, and C in a vertical column. All items are expanded. I move B to the right side of A and C right under B. This gives me 2 columns (A being one and B,C being the other). With everything being absolute, if I try to close B, then C doesn't move up - rightfully so.
I tried then making things "selectively" absolute, thereby flipping between relative and absolute but I got into a problem with coordinates. If you have a relative position and left:100px and top:-50px, then flipping the position to absolute causes these coordinates to be interpreted within an absolute context. My control flies off the screen. I tried fixing this by getting the absolute coordinates using jQuery's offset function, however this returns the relative coordinates and I'm stuck. I tried to maintain the absolute coordinates myself, but it didn't work either for some reason. It's getting out of control :).
Javascript
This javascript bind is called when the page is loaded. I bind this function to a PNG arrow so that when the arrow is pressed, the content in the appropriate div expands and contracts.
$('.ArrowMargin').bind('click', function () {
var splits = this.src.split("/");
var action = "";
if (splits.length >= 2) {
var folder = splits[splits.length - 2];
var image = splits[splits.length - 1];
if (folder == "Images") {
if (image == "arrow_open.png") {
action = "close";
this.src = "Images/arrow_closed.png";
} else {
action = "open";
this.src = "Images/arrow_open.png";
}
}
}
var divs = document.getElementsByTagName("div");
if (action != "") {
var options = {};
for (var i = 0; i < divs.length; i++) {
var element = divs[i];
if (element.className.indexOf("Hideable") != -1) {
if (this.parentNode.parentNode == element.parentNode) {
if (action == "open") {
var jQueryObj = jQuery(element);
jQueryObj.show("blind", options, 500, null);
} else {
var jQueryObj = jQuery(element);
jQueryObj.hide("blind", options, 500, null);
}
break;
}
}
}
CSS
This is the CSS stuff where I set up some simple styles. I have some empty styles that I use to access divs based on class.
.ArrowMargin { float:right; margin:0 5px 0 0; }
.alpha { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.bravo { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px; }
.delta { width:300px; background-color:#000000; border-color:#424242; border-width:1px; border-style:solid; color:#c59e32; -moz-border-radius: 15px;}
.charlie{}
.echo{}
HTML BODY
This is the body. It's just a number of divs that represent different pieces of content. The divs marked with the "Hideable" class are those that are jQuery blinded.
<body>
<div class="alpha">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar1</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable Center"></div>
</div>
<div class="bravo">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar2</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable charlie"></div>
</div>
<div class="delta">
<div class="LeftColumnCellTitle">
<span class="TitleMargin">foobar3</span>
<img class="ArrowMargin" src="Images/arrow_open.png" alt="Open"/>
</div>
<div class="ui-widget-content ui-corner-all Hideable echo"></div>
</div>
</body>

Need clean truncation of text for showing more-less

Using this javascript to show more-less, is there a simple way to make the text cut off clean so that it displayes whole lines of text (doesn't slice them horizontally)?
<div id="description" style="height: 20px; overflow: hidden">
Lots of text.
</div>
Show more/less>>
<script>
var open = false;
$('#more-less').click(function() {
if (open) {
$('#description').animate({height:'20px'});
}
else {
$('#description').animate({height:'100%'});
}
open = !open;
});
If it makes it easier I can truncate on <br /> tags.
Change 20px to a value in ems, such as 2em. One em is (approximately?) equal to the height of one line. You should also set the margin and padding on the p tag in ems.

Categories