Setting limit for mootools makeResizable dynamically? - mootools

I'm trying to set the upper limit values of a resizable image to keep it within the containing div. I'm using mootools to make the image both moveable and resizable (implementing Drag.Move and makeResizable to do so.)
My temporary solution is to use overflow:hidden; so the resized image does not overtake the rest of the page when it is sized beyond the container, but I'd like to be able to have a way so the image can not be resized outside of its container.
I know that since limit is set on 'domready', if I try to set it to a variable that changes value as the image is resized (ie: onDrag), the limit parameter won't be updated on the fly. I'm wondering if anyone has any insight into how I can achieve a similar effect to the Drag.Move container parameter, as makeResizable doesn't seem to have the same parameter.
HTML:
<div id="pImageArea">
<div id="pLogo" class="displayNone">
<div id="moveHandleName">
<img src="uploadedlogo.jpg" id="imgName" />
</div>
<div id="resizeHandleName"></div>
</div>
</div>
CSS:
#imageArea {
float: left;
width: 630px;
height: 400px;
border: 1px solid #333;
position: relative;
overflow: hidden;
}
#imgContainer {
width: 50px;
height: 50px;
border: 1px dashed #333;
position: absolute;
}
#imgName {
width: 100%;
}
#moveHandleName {
width: 100%;
height: 100%;
}
#resizeHandleName {
width: 8px;
height: 8px;
border: 1px solid #000;
position: absolute;
left: 100%;
top: 100%;
margin: -5px 0 0 -5px;
background-color: #fff;
z-index: 100;
}
JS:
window.addEvent('domready', function(){
var xLim = 50;
var yLim = 50;
// Make image moveable
new Drag.Move($('imgContainer'), {
container: $('imageArea'),
handle: $('imgHandleName')
});
// Make image resizable
$('imgContainer').makeResizable({
handle:$('handleName'),
limit: {x: [50, xLim], y: [50, yLim]},
onDrag: function(el) {
// Set imgContainer height
el.setStyle('height', $('imgName').getSize().y + 'px');
// Set upper limits
xLim = $('imageArea').getSize().x - el.getSize().x;
yLim = $('imageArea').getSize().y - el.getSize().y;
},
});
});
Thanks in advance,
Matt

I 'solved' it like this in my own code (modified to use your element and values):
$('imgContainer').retrieve('resizer').setOptions({
limit: {
x: [50, xLim],
y: [50, yLim]
}
});

The move limit should be properly enforced once you specify the 'container' option and your container has a set width and height. When you resize, my draggable does not need to have limits (re)set. (Using Moo 1.4 by the way.)
However, resizing does cause problems in conjunction with move and limits. The key is that the 'limit' option is only set when initializing makeResizable(). The only way to update it is by setting it with the code shown above. But you have to update it right after you dropped the draggable, because that's the only event that affects the limit. So:
// Make image moveable
new Drag.Move($('imgContainer'), {
container: $('imageArea'),
handle: $('imgHandleName'),
onDrop: function() {
$('imgContainer').retrieve('resizer').setOptions({
limit: {
x: [50, $('imageArea').getSize().x - parseInt($('imageArea]).getStyle('left'))],
y: [50, $('imageArea').getSize().y - parseInt($('imageArea]).getStyle('top'))]
}
});
});
As you can see, I've also used getStyle() instead of getPosition() because getPosition() returns a value relative to the window, while Moo sets the draggable top and left relative to the droparea.

Related

How to use negative padding in css

I want to add negative padding in css, I have written a small code of battery charging cell. What I want is if I enter value in negative like -1px than the cell color should move to the left side and div should stay in center.
.cell {
width: 100px;
height: 30px;
border: 1px solid black;
}
.padding {
background-color: #3D9970;
width: 10px;
float: left;
height: 30px;
position: absolute;
left: 55px;
padding-right: 1px;
}
<div class="cell">
<div class="cell1"></div>
<div class="padding"></div><span style="display: inline;">
</div>
Please help me.
You can't.
See the specification:
Unlike margin properties, values for padding values cannot be negative.
I think you can achieve the same effect with pseudo elements:
.cell{
display:block;
width: 100px;
height: 30px;
position:relative;
}
.cell:before{
content:'';
background-color: #3D9970;
width: 10px;
top:0;
left:calc(50% - 5px);
height: 100%;
position: absolute;
}
.cell:after{
content:'';
border: 1px solid black;
width:100%;
height:100%;
display:block;
top:0;
left: 0px;
position: absolute;
}
<div class="cell">
</div>
"Left" property could be negative, so if you change it you can move the position of the green rectangle in the middle (.cell:before) of the block and border itself (.after)
The easiest way is to use an absolute positioning relatively to a parent node. Here the parent node would be the battery "housing".
So you can set the position CSS value of the rot div to relative, and then the charge one to absolute. Indeed, according to MDN Webdocs:
absolute: [...] It is positioned relative to its closest positioned ancestor, if any.
Then, you just have to play with the left and width CSS properties. For the "middle" case, I chose to display one border.
Below a working snippet. Just click the "Begin the charge variation" button to start the show.
var chargeElement = document.getElementById("charge");
// To set a charge to the battery, simply call: setCharge(percentage)
function setCharge(percentage) {
var left;
var width;
if (percentage > 100) percentage = 100;
if (percentage < 0) percentage = 0;
chargeElement.setAttribute("data-value", percentage);
// If the charge is 50%, simply draw a line
if (percentage == 50) {
chargeElement.className = "middle";
} else {
chargeElement.className = "";
}
// Otherwise, adjust left and width values
if (percentage >= 50) {
left = 50;
width = percentage - left;
} else {
left = percentage;
width = 50 - left;
}
// Then update the charge style.
chargeElement.style.left = left + "%";
chargeElement.style.width = width + "%";
}
// A simple function to add / remove some charge
function addCharge(percentage) {
var value = parseInt(chargeElement.getAttribute("data-value"));
value += percentage;
setCharge(value);
}
// Here just some stuff for illustration.
// You don't need those functions to set the charge.
function letsBeginTheShow(buttonElement) {
buttonElement.disabled = true;
setNextCharge(10);
}
function setNextCharge(increment) {
var percentage = parseInt(chargeElement.getAttribute("data-value"))
percentage += increment;
if (percentage > 100) {
percentage = 100;
increment = -5;
}
if (percentage < 0) {
percentage = 0;
increment = 5;
}
setCharge(percentage);
setTimeout(function() {
setNextCharge(increment);
}, 50);
}
setCharge(50);
.battery {
position: relative;
width: 100px;
height: 30px;
border: 1px solid black;
/* Below : only for aestethic reasons */
float: left;
margin-right: 30px;
/* End of aesthethic stuff */
}
#charge {
height: 100%;
position: absolute;
background-color: #3D9970;
border-color: #3D9970;
}
.middle {
border-left: 1px solid;
}
<div class="battery">
<div id="charge" data-value="50" class="middle"></div>
</div>
<button onclick="letsBeginTheShow(this)">Begin the charge variation</button>

Vue.js: do not show element until it is in correct position

I am learning Vue and really enjoying it. I have a tab that I want fixed to the bottom of the browser window when the page loads. When a user clicks the tab, it will slide up to show some content.
Everything is working great. I am able to have the tab stick to the bottom of the page - and click events are working great as well.
The problem I am having is that I need to calculate the height of tab (and div) to set the CSS property correctly. When the page loads, you can see the tab slide down into place. I would like to hide the tab until everything has been calculated and it's in the correct place.
Here is what I'm using:
app.js
new Vue({
el: '#info',
delimiters: ['${', '}'],
data: {
active: false,
inactive: true,
styles: {
'bottom': 0
},
},
methods() {
toggle: function (event) {
event.preventDefault();
this.active = !this.active;
this.inactive = !this.inactive;
}
},
mounted() {
let tabHeight = this.$refs.infoTab.clientHeight;
let boxHeight = this.$refs.infoBox.clientHeight; // 473px
this.styles.bottom = -boxHeight + 'px';
}
});
HTML
<div class="info not-active" id="info" #click="toggle" ref="infoTab"
v-cloak
v-bind:class="{ active: active }"
v-bind:style="styles">
<!-- content -->
</div>
style.css
[v-cloak] {
display: none;
}
/* more classes */
.info {
width: 100%;
position: fixed;
&.inactive {
bottom: -100%;
}
&.active {
bottom: 0 !important;
}
}
I know I am close, I just don't want users to see the tab slide into place. It should just be there. I tried using the created hook, but clientHeight is not available.
Any suggestions are greatly appreciated!
I think you can solve this just using CSS, no need to use any of Vue's lifecycle hooks, I made a pen with a vanilla JS example:
let infoNode = document.getElementById('info');
infoNode.addEventListener('click', () => {
if (infoNode.style.top) {
// clear inline top style
infoNode.style.top = '';
} else {
// set top to client height + 2 * border thickness
infoNode.style.top = `calc(100% - ${infoNode.clientHeight}px - 4px)`;
}
});
#info {
font-size: 16px;
width: 200px;
border: 2px solid hsl(0, 0%, 80%);
padding: 8px;
border-radius: 4px;
cursor: pointer;
position: fixed;
/* 100% height of the viewport subtracting:
tab height: padding, margin, & font size */
top: calc(100% - (8px + 8px + 24px));
/* we center the tab horizontally here using
50% the width of the viewport - 50% the fixed
width of the tab */
left: calc(50% - 200px/2);
transition: top 0.5s;
}
.title {
font-size: 24px;
font-weight: 500;
margin-bottom: 8px;
display: block;
}
p {
margin: 0;
}
<div id="info">
<span class="title">Click on Me</span>
<p>
This is the content of the tab, isn't it great? I think so too, and it can be of any arbitrary length!
</p>
</div>
Basically the trick is to use calc with top instead of -100% with bottom for your positioning, then your tab is initially rendered in the correct position and you don't have to worry it being out of place when a visitor first loads your page.

Sidebar that changes content width

I am currently developing a plugin for existing websites.
Its purpose is to display a sidebar with my content. To that end, the website owner creates an empty div, references my javascript file and calls my code with the ID of the empty div.
My plugin is then creating an iFrame in that empty div and loads its content. It also is responsible for styling the provided div so that it actually is a sidebar: It changes the width and height of that div and attaches it to the right edge of the screen.
So, all of that is basically working - loading my iFrame and styling the div.
The problem is that I am not satisfied with the result.
I have tried two different styles for the div:
Approach 1: float right
I used this CSS:
float: right;
height: 100%;
width: 100px;
The problem with this is that it doesn't change the total width of the rest of the page. In other words, elements on the website with a width: 100% will be shown below my sidebar.
https://jsfiddle.net/DHilgarth/mmzefm14/
Approach 2: Absolute positioning
I used this CSS:
position: absolute;
top: 0;
right: 0;
height: 100%;
width: 100px;
This approach has the problem that my sidebar now simply overlaps the controls from the website.
https://jsfiddle.net/DHilgarth/34hmnw9h/1/
Is there a way to achieve what I want? A sidebar that basically reduces the available size of the body for all elements, except mine?
I have now chosen to actually do exactly what I asked for: I reduce the available width of the body tag.
This is not trivial because of box-sizing, padding, margin, border etc and I am sure I have missed a lot of edge cases but for now, the following logic is working for me:
If box-sizing is border-box: set the right padding of the body element to the width of my sidebar.
Otherwise, set the width of the body element to the width of the body element minus the width of the sidebar. On resize of the window, the width of the body has to be adjusted accordingly.
Code:
function initSidebar() {
loadSidebar("sidebar");
}
// This code would be loaded from a javascript file I provide
function css(element, property) {
return window.getComputedStyle(element, null).getPropertyValue(property);
}
function getSidebarWidth(sidebarElement) {
var boundingRect = sidebarElement.getBoundingClientRect();
return boundingRect.right - boundingRect.left;
}
function styleBorderBoxBody(bodyElement, sidebarElement) {
bodyElement.style.paddingRight = getSidebarWidth(sidebarElement) + "px";
}
function resizeBody(bodyElement, previousWindowWidth, previousBodyWidth) {
var currentWindowWidth = window.innerWidth;
var newBodyWidth = previousBodyWidth - previousWindowWidth + currentWindowWidth;
bodyElement.style.width = newBodyWidth + "px";
return {currentWindowWidth, newBodyWidth};
}
function styleBody(bodyElement, sidebarElement) {
var boxSizing = css(bodyElement, "box-sizing");
if(boxSizing == "content-box" || !boxSizing || boxSizing == "") {
var sidebarWidth = getSidebarWidth(sidebarElement);
var width = bodyElement.clientWidth - sidebarWidth;
bodyElement.style.width = width + "px";
sidebarElement.style.right = (-sidebarWidth) + "px";
var windowWidth = window.innerWidth;
window.addEventListener("resize", function(e) {
var newWidths = resizeBody(bodyElement, windowWidth, width);
width = newWidths.newBodyWidth;
windowWidth = newWidths.currentWindowWidth;
});
} else if(boxSizing == "border-box") {
styleBorderBoxBody(bodyElement, sidebarElement);
window.addEventListener("resize", function(e) { styleBorderBoxBody(bodyElement, sidebarElement); });
}
}
function loadSidebar(sidebarId) {
var sidebarElement = document.getElementById(sidebarId);
sidebarElement.className = "sidebar";
var bodyElement = document.getElementsByTagName("body")[0];
styleBody(bodyElement, sidebarElement);
}
// end: my code
initSidebar();
* {
margin: 0;
padding: 0;
}
html {
}
*,
*:before,
*:after {
box-sizing: inherit;
}
body {
font: 14px/1.1 Helvetica, Sans-Serif;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
height: 100%;
}
#editor {
width: 100%;
height: 500px;
}
/* this class would be loaded from a CSS file I provide */
.sidebar {
border-color: green;
border-style: solid;
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: 100px;
}
<div id="sidebar"></div>
<h1>Some UI from the existing website</h1>
<textarea id="editor">The text area</textarea>

how to disable dragend animation in html5

I created a draggable element by setting its draggable attribute. When I drop the element, there is an animation of the element snapping back to its origin position:
How can the snap-back animation be disabled? I tried calling preventDefault() on the dragend event, but it had no effect on the animation.
The following snippet shows the basics:
document.getElementById('test').addEventListener(
'dragend', evt => {
evt.preventDefault();
}
);
#container {
border: 1px solid black;
min-width: 300px;
min-height: 200px;
position: relative;
}
#test {
width: 50px;
height: 50px;
background-color: red;
position: absolute;
top: 25px;
left: 40px;
}
<div id="container">
<div id="test" draggable='true'></div>
</div>
Not every browser will show the dragged #test jumping back to the original position.
In order to prevent the animation, you need the drop event to fire. For the drop event to fire, you need to call preventDefault() in the handler for dragover.
document.addEventListener('dragover', function(e) { e.preventDefault() })
Example in MDN docs shows the same thing: https://developer.mozilla.org/en-US/docs/Web/Events/drop#Example
An old blog post describing the quirks of HTML5 Drag and Drop API: https://www.quirksmode.org/blog/archives/2009/09/the_html5_drag.html
As was said earlier, you need to explicitly describe onDragOver handler on the parent's container (where you will drop your draggable element) and put .preventDefault() on event to prevent this animation.
Here is a simple React code example for better understanding of this mechanic (you can position the box inside the container by dragging it):
App.jsx
import './App.css'
const App = () => {
function handleDragOver(e) {
e.preventDefault()
}
function handleDrop(e) {
let box = document.getElementById('box')
if (box) {
box.style.top = e.clientY + 'px'
box.style.left = e.clientX + 'px'
}
}
return (
<div className="container" onDragOver={handleDragOver} onDrop={handleDrop}>
<div id="box" draggable></div>
</div>
)
}
export default App
App.css
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
.container {
width: 100vw;
height: 100vh;
position: relative;
}
#box {
width: 100px;
height: 100px;
background-color: lightgreen;
position: absolute;
}

HTML5/Cesium - making divs float over cesium map

I am using cesium : http://cesiumjs.org/
and I wanted to make some divs float over a cesium map, but I can't get it to work.
I tried the following container/tag method at jsfiddle.net/j08691/dChUR/5/ - substituing the image by a cesium map div - but it doesn't seem to work - the "tag" div isn't shown.
Any help?
You need to add position: absolute; and either top or bottom to your CSS, because the widget also uses absolute positioning. Adding this creates a new stacking context, which overrides z-index.
Here's a working example, hit "Run Code Snippet" at the bottom of this:
Cesium.Camera.DEFAULT_VIEW_FACTOR = 0;
var viewer = new Cesium.Viewer('cesiumContainer', {
timeline: false,
animation: false,
navigationHelpButton: false
});
var skyAtmosphere = viewer.scene.skyAtmosphere;
var skyCheckbox = document.getElementById('skyCheckbox');
skyCheckbox.addEventListener('change', function() {
viewer.scene.skyAtmosphere = skyCheckbox.checked ? skyAtmosphere : undefined;
}, false);
html, body, #cesiumContainer {
width: 100%; height: 100%; margin: 0; padding: 0; overflow: hidden;
font-family: sans-serif; color: #edffff;
}
#controlPanel {
position: absolute;
top: 5px;
left: 5px;
background: rgba(42, 42, 42, 0.8);
padding: 5px 8px;
border-radius: 5px;
}
label {
cursor: pointer;
}
label:hover span {
text-decoration: underline;
}
<link href="http://cesiumjs.org/releases/1.15/Build/Cesium/Widgets/widgets.css"
rel="stylesheet"/>
<script src="http://cesiumjs.org/releases/1.15/Build/Cesium/Cesium.js">
</script>
<div id="cesiumContainer"></div>
<div id="controlPanel">
This is a floating control panel<br/>
with a translucent background color.
<p>
<label>
<input id="skyCheckbox" type="checkbox" checked />
<span>Enable atmospheric effect</span>
</label><br/>
<button class="cesium-button">Button 1</button>
<button class="cesium-button">Button 2</button>
</p>
</div>
To add to emackey's answer, what I had to do in addition to adding position: absolute to my css was to add a top:150px or bottom:150px. Basically anything that will specify a position relative to the parent container.
Even though using the absolute position it is most likely being pushed down by the cesium widget since it takes up 100% height.