Positioning an element outside the containing element - javascript

I have a webpage with code I cannot change which is set up something like this:
<table style="width: 750px; margin-left: auto; margin-right: auto">
<tr>
<td>
<table>
<tr><td><div id=1></td></tr>
<tr><td><div id=2></td></tr>
<tr><td><div id=3></td></tr>
<tr><td><div id=4></td></tr>
</table>
</td>
</tr>
</table>
I'd like to add an element (via userscript) to the right of the outer table which lines up with div 1, 2, 3, or 4. Is there a way to do this? Maybe measure the current position of the elements then absolutely position relative to the outer table?
(The actual HTML is far more messy; there are many more siblings and parents, but this is the heart of the code.)

Position an element adjacent to table rows
Create and append the desired element to your table. Then create and add custom styles that reflect the position of the table row. I've written down some comments below to help navigate the code.
// Setup additional element
const yourContent = document.createElement("div");
yourContent.setAttribute("class", "content");
const yourText = document.createTextNode("Lorem ipsum");
yourContent.appendChild(yourText);
// Find the element you would like to attach to
const graphElem = document.getElementsByClassName("rows");
// Set the styles to be added
const cssTemplateString = `td > .rows > .content{
position: absolute;
left: ${graphElem[0].clientWidth}px; // For element to appear on the left side set to "-" ( negative value )
height: calc(${graphElem[0].clientHeight}px +2px); // The 2 extra pixels are for the border
width: calc(${graphElem[0].clientWidth}px + 2px);
background: white;
padding: 3rem;
border: 1px solid gray;
top: 0;
}`;
const styleTag = document.createElement("style");
styleTag.innerHTML = cssTemplateString;
// Add styles to the head tag
document.head.insertAdjacentElement("beforeend", styleTag);
// Only required for this example
for (let i = 0; i < graphElem.length; i++) {
graphElem[i].addEventListener("click", function (event) {
event.target.appendChild(yourContent);
});
}
td > .rows {
border: 1px solid gray;
width: 8rem;
padding: 3rem;
background: whitesmoke;
}
td {
position: relative; /* The additional content will be positioned relative to this */
}
Demo : https://jsfiddle.net/hexzero/82bpaL4e/
If you click on the table row in the demo, the content will appear on the right.

Related

Center an absolute element below a div?

I'm trying to center a pargraph below a div (could be a square image for the matter). I understand that the easiest way to do it is to contain both the div and the text below it in a single container and use text-align, but in this instance I have a limitation where I cannot let the small image be contained in a container wider than that image.
Without centering the text it looks like this:
My code:
HTML:
<div class="block"></div>
<p class="label">This text is a bit long</p>
CSS:
body {
padding: 5rem;
}
.block {
background-color: blue;
width: 80px;
height: 80px
}
.label {
}
Codepen:
https://codepen.io/omerh3/pen/oNqVjvV
The reason why I cannot let the image be in a container is that I'm using ReactFlow where the handles should connect to the sides of the image without a gap. If I put the image and the text inside a div, the div will take the width of the text and thus it will naturally be wider than the image.
I tried centering the text below the image with absolute positioning, but with different paragraphs sizes, it won't be persistent in the center. Is there a away to achieve this without inserting the image/square and the text inside one div?
One last thing: the width of the image if constant, for example 100px
one way to do this is to get the coordinates of your two elements and then add margin-left: to adjust the position of the span
let divOffsets = document.getElementById('a').getBoundingClientRect();
let divRight = divOffsets.right;
let divLeft = divOffsets.left;
console.log(divLeft,divRight)
let spanOffsets = document.getElementById('b').getBoundingClientRect();
let spanRight = spanOffsets.right;
let spanLeft = spanOffsets.left;
console.log(spanLeft,spanRight)
let divCenter = divLeft + divRight / 2
console.log(divCenter)
let offset = divCenter - (spanLeft + spanRight / 2)
offset = offset + "px"
document.getElementById('b').style.marginLeft = offset;
body {
padding: 5rem;
border:solid 1px red;
position:relative;
}
.block {
background-color: blue;
width: 80px;
height: 80px;
}
span{
position:absolute;
}
<div id = 'a'class="block"></div>
<span id = 'b' >1234</span>
Do you mean something like this??
body {
padding: 5rem;
}
.block {
background-color: blue;
width: 80px;
height: 80px;
margin:0 auto;
}
p.label {text-align: center}
<div class="block"></div>
<p class="label">This text is a bit long</p>

Pop up warning in center of browser when value exceeds number

I have a page pulling SNMP data (using php) and then displaying it via HTML and color coding the values. I'd like to add a pop up alert when value exceeds a specific number.
I have tried various jquery options to make this happen but its not working.
PHP part to obtain value:
<?php
$valueis = snmp2_get("IPADDRESS", "public", ".1.3.6.1.4.1.476.1.42", 1000000, 0);
?>
HTML part:
<html>
<meta HTTP-EQUIV="REFRESH" content="20">
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script type="text/javascript">
$(document).ready(function(){
$('#table_fid td.mynumber').each(function(){
if ($(this).text() <= '16' ) {
$(this).addClass('blue');
}
if ($(this).text() > '16' ) {
$(this).addClass('green');
}
});
});
<DIV style="position: absolute; top:10px; left:10px; width:10px; height:10px"><table id="table_fid">
<td class="mynumber"><a href=http://mywebsite.com><?php echo $valueis?></a></td></tr>
</table></DIV>
This works great.
However I want it that when value is higher than 16, it also shows a pop up window in the browser as an alert. I've tried to incorporate the guide in this page to auto trigger but with no luck: https://html-online.com/articles/simple-popup-box/. The pop up window in this page is exactly how I wish mine to be.
This solution is for MODERN browsers, supporting rgba(). Older browsers require some more advanced CSS.
You should ideally be accessing the PHP value through AJAX, but you can hardcode the PHP value(s) in the JS section to make things easier, and then insert the value(s) into the DOM object(s) (td.mynumber).
Your example only shows one row of data... but considering you used an $.each() iterator, you may be simplifying a solution for multiple rows?
For a single row:
<html>
<head>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script>
//first assign the PHP value
var the_value = <?php echo $valueis?>; //notice NO quotes! because this is numeric!
//now you can use 'the_value' instead of reading from the DOM object
$(document).ready(function(){
$('#table_fid td.mynumber').each(function(){
//assign the_value to the DOM object
$(this).children().text(the_value);
//add classes based on the_value
if (the_value <= 16 ) {//the_value is NUMERIC - no quotes!
$(this).addClass('blue');
}
else {//you don't need another 'if' here, value must be higher than 16
$(this).addClass('green');
//show overlay
$('#overlay').show()// changes display: none to block
}
});
});
function closeOverlay () {
$('#overlay').hide()// changes display: block to none
}
</script>
<style>
*,*:before,*:after{/*best practice, reset box-sizing to include borders and padding in width!*/
box-sizing: border-box;
}
body{/*best practice, reset body container values*/
margin: 0;
padding: 0;
}
#table-container{
position: absolute;
top:10px;
left:10px;
width:10px;
height:10px;
z-index: 1; /*Make this render as the bottom layer*/
}
#overlay{
display: none; /* default state hidden */
position: fixed; /* does not move */
top: 0;/* place at top */
left: 0;/*place at left */
height: 100%;/* full height of container */
width: 100%;/* full width of container */
background: rgba(0,0,0,0.5);/* semi-transparent black background */
z-index: 2;/*Make this render ABOVE the bottom layer, because 2 is greater than 1!*/
}
#overlay-x{
height: 32px;
width: 32px;
border-radius: 16px;/*border-radius of HALF height makes this a circle!*/
display: block;
font-family: Arial;
background: white;
line-height: 26px;
font-size: 15px;
font-weight: bold;
text-align: center;
border: 3px solid #ccc;
/* now position this white circle */
position: absolute;/* absolutely positioned */
top: 0; /*place at top */
right: 0;/*place at right*/
margin-top: -10px;/*pull UP 10px*/
margin-right: -10px;/*pull RIGHT 10px*/
cursor: pointer;/*show pointer on hover to make it LOOK clickable*/
}
/* fixed-size */
#overlay-message-container{
width: 300px;
height: 200px;
background: white;
text-align: center;
/* this is how you center position fixed-size */
position: absolute;/* absolutely positioned */
top: 50%; /* place in absolute center of container height */
left: 50%;/* place in absolute center of container width */
margin-top: -100px; /* pull exactly HALF of the HEIGHT UPward*/
margin-left: -150px; /* pull exactly HALF of the WIDTH LEFTward*/
padding: 80px 10px;
box-shadow: 0 0 30px 10px rgba(0,0,0,0.25);/*drop shadow effect*/
}
</style>
</head>
<body>
<div id="table-container"><!--moved styles to HEAD-->
<table id="table_fid">
<tr>
<td class="mynumber"><!--don't need PHP here, value is assigned by JS!--></td>
</tr>
</table>
</div>
<div id="overlay"><!--the overlay-->
<div id="overlay-message-container"><!--the message container-->
<div id="overlay-x" onclick="closeOverlay()">X</div><!--the X to close the window-->
<div id="overlay-message">This is the message inside the overlay!</div>
</div>
</div>
</body>
</html>
If you have multiple rows being printed, you'd assign an array of JS values:
<script>
//first assign the PHP values - assuming 4 values
var the_values = [
<?php echo $valueis_1?>, //this is a list of value, use commas
<?php echo $valueis_2?>,
<?php echo $valueis_3?>,
<?php echo $valueis_4?>
]
//now you can use the_values instead of reading from the DOM object
//Note: $.each() is passed an 'index' value which returns the current loop iteration; you can use this to assign array values
$(document).ready(function(){
$('#table_fid td.mynumber').each(function(index){//note 'index'!
//assign the_value to the DOM object
$(this).text(the_values[index]);
//add classes based on the_values[index]
if (the_values[index] <= 16 ) {//the_values[index] is NUMERIC - no quotes!
$(this).addClass('blue');
}
else {//you don't need another 'if' here, value must be higher than 16
$(this).addClass('green');
//show overlay - doesn't matter if it's already showing!
$('#overlay-message').show()
}
});
});
</script>
And then in your HTML, you'd need the 4 rows added:
<div id="table-container"><!--moved styles to HEAD-->
<table id="table_fid">
<tr>
<td class="mynumber"><!--don't need PHP here, value is assigned by JS!--></td>
</tr>
<tr>
<td class="mynumber"><!--don't need PHP here, value is assigned by JS!--></td>
</tr>
<tr>
<td class="mynumber"><!--don't need PHP here, value is assigned by JS!--></td>
</tr>
<tr>
<td class="mynumber"><!--don't need PHP here, value is assigned by JS!--></td>
</tr>
</table>
</div>
Proof here:
https://codepen.io/remedio/pen/pozbdLY
This is a simplified answer for a FIXED-SIZE container. There is some cool stuff you can do with pseudo-elements and inline-blocks to make dynamic sized elements centered....

why is child element's width changing when parent's width changes?

I am trying to create a tooltip element that has a min width of 50px and a max width of 200px. I place the tooltip element inside another element so that I can easily control when the tooltip appears or disappears when there is a hover event on the parent.
The problem that I have is that the tooltip element's width appears to be controlled by the parent's width even though I specified that the child(tooltip) has an absolute position.
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
position: relative;
transition: width 2s;
}
#tooltip {
position: absolute;
top: calc( 100% + 5px );
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
I would like the tooltip (yellow div) to keep it's size at 200px in this example, but we can see that when the parent changes width, the tooltip width also changes. Why?
Is there a way to fix this problem?
Clarification: In this example: https://codepen.io/anon/pen/ePPWER we see that the tooltip text looks nice on one line. I don't want the tooltip's div to change its width when the parent changes width, because it forces the tooltip text to wrap onto 2 lines which is undesirable.
If we check the specification related to the width of absolutely positioned element we can read this:
'width' and 'right' are 'auto' and 'left' is not 'auto', then the width is shrink-to-fit . Then solve for 'right'
So in your case the width of your element is shrink to fit:
Calculation of the shrink-to-fit width is similar to calculating the
width of a table cell using the automatic table layout algorithm.
Roughly: calculate the preferred width by formatting the content
without breaking lines other than where explicit line breaks occur,
and also calculate the preferred minimum width, e.g., by trying all
possible line breaks. CSS 2.1 does not define the exact algorithm.
Thirdly, calculate the available width: this is found by solving for
'width' after setting 'left' (in case 1) or 'right' (in case 3) to 0.
Then the shrink-to-fit width is: min(max(preferred minimum width,
available width), preferred width).
To make it easy, and without considering the min/max-width, the width of your element will try to fit the content without exceding the width of its parent container (containing block). By adding min/max-width you simply add more constraint.
One idea of fix it to remove positon:relative from the parent element so that it's no more the containing block of the position:absolute element (it will be the initial containing block which is wide enough to avoid the available width constraint).
Then use margin instead of top/left to control the position:
let p = document.getElementById( 'parent' );
let b = true;
setInterval( ()=> {
b = !b;
let w = 10;
if( b ) {
w = 300;
}
p.style.width = `${w}px`
}, 5000 );
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
}
#tooltip {
position: absolute;
margin-top: 30px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="parent">
<div id="tooltip">
My long tooltip text that wraps to multiple lines as needed.
</div>
</div>
ID Tooltip is being used under Parent. When parent's width changes, it also suggest that tooltip's total width is changed. Since you have used mix-width and max-width it will expand till it reaches max-width. If you want it to be fixed then simple use width.
It is because the .parent has a position: relative. This will keep all children (position: absolute included) as confined by the parent div.
Not sure if this will work for you because it is pulling the tooltip out of the parent and making it's own with span wrapping the text. Alternatively, you'll need to change the parent from being relative otherwise it'll continually affect the child.
let p = document.getElementById('parent');
let b = true;
setInterval(() => {
b = !b;
let w = 10;
if (b) {
w = 300;
}
p.style.width = `${w}px`
}, 5000);
#parent {
background-color: cyan;
width: 100px;
height: 25px;
transition: width 2s;
position: relative;
}
#root {
position: relative;
}
#tooltip {
width: 100%;
}
#tooltip span {
position: absolute;
top: calc( 100% + 5px);
left: 5px;
min-width: 50px;
max-width: 200px;
background-color: yellow;
padding: 5px;
border: 1px solid black;
}
<div id="root">
<div id="parent"></div>
<div id="tooltip">
<span>My long tooltip text that wraps to multiple lines as needed.</span>
</div>
</div>

Setting the height of a textarea inside a table

I've set the height of my textarea to 500%, but for some reason it's not changing to 500%. I think it has something to do with it being inside a table, but I'm not sure what to change set the height correctly. For some reason, the width of the textarea CAN be change inside the table.
table,td {
border: 1px solid black;
}
textarea {
resize: none;
width: 100%;
height: 500%;
}
<table>
<tr>
<td>
firstTD
</td>
<td>
<form method = 'POST' action = 'updateProfile.php'>
<textarea id = 'textarea' placeholder = 'type something here...' onfocus = \"this.placeholder = ''\" onblur = \"this.placeholder = 'type something here...'\" maxlength = '10000'></textarea>
</form>
</td>
</tr>
</table>
The other answer solves the problem, but doesn't explain it. When you use % to define width/height in CSS, you are making it whatever% of that element's container' width/height.
When you set your textarea to be 100% of an otherwise empty <td>, it's not going to be very big.
Setting it to posistion:absolute will work IF none the ancestor elements are positioned. A simpler approach would be to just use something other than % to define your width and height. Try width:10em; and adjust it until you get it right.
Edit.
To answer a question in the comments: The reason using % to define the height works in this case, is because the empty cell has a height, but not a width. An empty table cell still inherits the height of the row, which is as tall as the tallest <td>. In this case there is another <td> that has content, giving the cell a height.
So, If there was another row, and one of the cells in the same column had content, then width would work with % too.
That said, it's not a good idea to use % for width and height in a table, because when the content in the table changes, your percentages will change. Also, when you use % as opposed to px or em, it will not stretch the parent container.
Edit again
I honestly didn't even notice the form element. Then your percents are relative to the height/width of the form element, not the <td>. There must be some padding giving your cells width/height but the form wouldn't have any dimensions.
Try setting position: absolute to textarea and give a position: relative to the parent. Also remove width and give left and right values as 0. But remember, this will make the textarea to overflow out of the content. Is that what you are expecting?
table,
td {
border: 1px solid black;
position: relative;
width: 350px;
}
textarea {
resize: none;
height: 500%;
left: 0;
right: 0;
position: absolute;
top: 0;
}
<table>
<tr>
<td>
firstTD
</td>
<td>
<form method='POST' action='updateProfile.php'>
<textarea id='textarea' placeholder='type something here...' onfocus=\ "this.placeholder = ''\" onblur=\ "this.placeholder = 'type something here...'\" maxlength='10000'></textarea>
</form>
</td>
</tr>
</table>
Or if you need something like this?
table,
td {
border: 1px solid black;
position: relative;
width: 350px;
}
textarea {
resize: none;
height: 10em;
width: 100%;
display: block;
box-sizing: border-box;
}
<table>
<tr>
<td>
firstTD
</td>
<td>
<form method='POST' action='updateProfile.php'>
<textarea id='textarea' placeholder='type something here...' onfocus=\ "this.placeholder = ''\" onblur=\ "this.placeholder = 'type something here...'\" maxlength='10000'></textarea>
</form>
</td>
</tr>
</table>
With this you can set size for text area
$("textarea").css("height", $("textarea").parent("td").height())
.css("width", $("textarea").parent("td").width())

Moving tables made in HTML as a div to and from certain coordinates

My code is:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8>
<title>Inventory-Tags</title>
<script src="http://www.kryogenix.org/code/browser/sorttable/sorttable.js"></script>
<style>
/* Appearance */
body, table { font-family: sans-serif; }
table { border-collapse: collapse; }
td, th { padding: 6px; }
th { background: #333; color: white; }
tbody tr:nth-child(odd) { background: #dfdfdf; }
table { border: 1px solid red; }
/* Scrollability of table */
table { width: 610px; } /* fixed width table */
thead tr { display: block; } /* makes it sizeable */
tbody {
display: block; /* makes it sizeable */
height: 262px; /* height of scrollable area */
overflow: auto; /* scroll rather than overflow */
width: 100%; /* fill the box */
}
thead th { width: 197px; } /* fixed width for THs */
tbody td { width: 185px; } /* fixed width for TDs */
</style>
</head>
<body>
<script>
function myFunction(Move2) {
document.getElementById("table1").style = "position: absolute; top: 40px"
document.getElementById("table2").style ="position:absolute; top: -9999px"
}
</script>
<script>
function myFunction(Move2) {
document.getElementById("table1").style = "position: absolute; top: -9999px"
document.getElementById("table2").style ="position:absolute; top: 40px"
}
</script>
<a onclick="function(Move)">Hi</a>
<div style="left: 40px">
<a onclick="function(Move2)">Hi2</a>
</div>
<div id="table1"; style="position: absolute; top: 40px">
<h1><div id=user_name>'s Inventory- Tags</h1>
<table class="sortable">
<thead><tr><th>Name</th><th>Explanation</th><th>Rarity</th></thead>
<tbody>
<tr><td>(the Halfbaked)</td><td>It looks kinda... under-cooked</td><td>R2</td></tr>
<tr><td>(Ninja)</td><td>Hiding in the night, you approach.</td><td>R6</td></tr>
<tr><td>(the Foul)</td><td>Foresee the future.</td><td>R7</td></tr>
<tr><td>(the Master)</td><td>Make le gold.</td><td>R6</td></tr>
<tr><td>(the Photographer)</td><td>Where's the camera?</td><td>R5</td></tr>
<tr><td>(the Canonical)</td><td>We all ship it.</td><td>R5</td></tr>
<tr><td>(the Punching Bag)</td><td>Looks like that hurt.</td><td>R3</td></tr>
<tr><td>(the Fancy)</td><td>I swear, if you start singing that song...</td><td>R5</td></tr>
<tr><td>(the Knight)</td><td>You live by the code of chivalry.</td><td>R6</td></tr>
<tr><td>(the Samurai)</td><td>Your enemy is the ninja.</td><td>R6</td></tr>
<tr><td>(the Loser)</td><td>You're not a winner.</td><td>R2</td></tr>
<tr><td>(the Outlaw)</td><td>You done somethin' bad, son.</td><td>Unique</td></tr>
<tr><td>(the Lord)</td><td>We bow to you humbly.</td><td>R9</td></tr>
<tr><td>(the Fugitive)</td><td>Always running, always hiding.</td><td>Unique</td></tr>
<tr><td>(the Egg)</td><td>Yes, that's right. An egg.</td><td>R4</td></tr>
</tbody>
</table>
</div>
<div id="table2"; style="position: absolute; top: -9999px">
<h1><div id=user_name>'s Inventory- Specials</h1>
<table class="sortable">
<thead><tr><th>Name</th><th>Explanation</th><th>Rarity</th></thead>
<tbody>
<tr><td>/slap</td><td>Allows you to slap any player for up to 15 damage.</td><td>R8</td> </tr>
<tr><td>/heal</td><td>Allows you to automatically heal yourself.</td><td>R9</td></tr>
<tr><td>/buildmode</td><td>Grants immunity to damage and permanent lighting; meant for use during building.</td><td>R9</td></tr>
<tr><td>/buff</td><td>Allows you to give yourself buffs.</td><td>R4</td></tr>
<tr><td>/invasion</td><td>Allows you to start an invasion.</td><td>R8</td></tr>
<tr><td>/gbuff</td><td>Allows you to give buffs to all players.</td><td>R9</td></tr>
<tr><td>/spawnmob</td><td>Allows you to spawn in any mob.</td><td>R9</td></tr>
<tr><td>/command1</td><td>Allows you to use /command1.</td><td>R8</td></tr>
<tr><td>/command2</td><td>Allows you to use /command2.</td><td>R8</td></tr>
<tr><td>HouseName</td><td>Gives ownership to HouseName.</td><td>House</td></tr>
<tr><td>HouseName2</td><td>Gives ownership to HouseName2.</td><td>House</td></tr>
</tbody>
</table>
</div>
</body>
</html>
From my code, I'm sure you can see what I want. I want to be able to click on my 'a' divs. When they click on 'Hi' the first table goes to the visible area, and the second table goes away. When you click on 'Hi2' the first table goes away, and the second table goes to the visual area. I know that onclick won't work with <style>, so I tried using the ParseInt method, but that will continue to move. Note that I will have four tables in my full code, and four buttons. I don't make it so that if you click 'Hi,' and then 'Hi3,' you have to click 'Hi2' or 'Hi4' to display their respective tables.
In short, I want to be able to click a button, or an <a>, and move the four tables to my own specified coordinates, without using <style>.
To answer to fixing your problem is to understand the use of basic javascript.
You cannot change an elements style like this
document.getElementById("table1").style = "position: absolute; top: -9999px"
document.getElementById("table2").style ="position:absolute; top: 40px"
Each different style change has to be set individually. Like this.
document.getElementById("table1").style.top = "-9999px"; //also don't forget the semicolon
When you are setting multiple styles on an element, it is best to set the element as a variable and setting a style to this variable each time.
var elem = document.getElementById("table1");
elem.style.position = "absolute";
elem.style.top = "-9999px";
MOST styles can be placed after the elem.style.top using their normal style name such as top, height, color, etc.. Except z-index which can be defined as zIndex

Categories

Resources