I'm using a framework that requires explicit pixels for a modal window that will be used to display messages to the end user at various points in the system. The messages will always be plain text so I want to base the width on the number of chars in the message. A message could be 2 words or 2 paragraphs.
At first I tried straight multiplication, which works great for content.length of 150-300 but anything <150 is too small and >300 gets way too large.
I need help with an algorithm that would effect a smaller number greater than it would effect a large number. I would like it to have the rough effect of:
getWidthForContent("small message") = 120; //13 chars
getWidthForContent("another reasonable length for a modal message") = 300; //45 chars
getWidthForContent("lorem ipsum...") = 900; //600+ chars
Another way you could look at this is trying to find a multiplier value from a curve. Here is a really sloppy approach:
determineBestWidth: function (contentLength) {
var width = 900; //max
if (contentLength < 200) {
width = contentLength * 2;
if (contentLength < 125) {
width = contentLength * 3;
if (contentLength < 50) {
width = contentLength * 5;
if (contentLength < 20) {
width = contentLength * 10;
if (contentLength < 10) {
width = contentLength * 20;
}
}
}
}
}
return width;
},
forgive me for my lack of math skills
You need to create a hidden div with the same content of your modal.
Then with some CSS tricks you will be able to do what you want (if I understood what you wanted) :
var modal = document.getElementById("modal");
var modalHidden = document.getElementById("modalHidden");
modalHidden.innerHTML = modal.innerHTML;
var height = (modalHidden.clientHeight + 1) + "px";
var width = (modalHidden.clientWidth + 1) + "px";
var result = document.getElementById("result");
result.innerHTML = "Height : " + height + "<br/>Width : " + width;
if (parseInt(width.substring(0, width.length - 2)) < 500)
{
modal.style.width = width;
}
#modal {
border: 1px solid darkblue;
border-radius: 4px;
text-align: justify;
background-color: #a4b8ff;
padding: 10px;
width: 500px;
word-wrap :break-word;
margin-bottom: 40px;
}
#modalHidden
{
visibility: hidden;
white-space: nowrap;
width: auto;
height: auto;
position: absolute;
}
#info {
color: grey;
margin-bottom: 40px;
}
<div id="info">Add some text and see that the width changes. Just use the variable in the JS and you're good.</div>
<div id="modal">
Some short text
</div>
<div id="modalHidden"></div>
<div id="result"></div>
I made a jsFiddle, if you wan to "play" with it.
Credits go to Bacon Ipsum.
Related
I have 2 boxes / divs. An outer box and an inner box. The inner box is always contained within the outer box. Given the following info:
outer box: width=200 height=100
inner box: aspect ratio=16/9
In JavaScript, how do I calculate the maximum size of the inner box such that its aspect ratio is preserved?
I know you're asking for JavaScript specifically, but this is pretty simple to do in CSS with aspect-ratio, and if you need the dimensions in JS you could just grab the rendered dimensions.
const pre = document.querySelector('pre');
const aspect = document.querySelector('.ratio');
// getboundingClientRect also works.
const dimensions = window.getComputedStyle(aspect);
pre.textContent = `width: ${dimensions.width}, height: ${dimensions.height}`;
.container {
width: 200px;
height: 100px;
}
.ratio {
aspect-ratio: 16 / 9;
/* Just keeping it within the constaints */
max-height: 100px;
border: 1px solid red;
}
.no-ratio {
border: 1px solid red;
}
<div class="container">
<div class="ratio">
16:9
</div>
</div>
<pre></pre>
<div class="container">
<div class="no-ratio">
Not 16:9.
</div>
</div>
<pre></pre>
Get the outer aspect ratio and use that to determine if the inner box needs to be letter-boxed (landscape, shorter) or pillar-boxed (portrait, narrower) relative to the outer box. Calculate the inner dimensions based on that. You can also calculate the offsets needed to center it.
const outerWidth = 200;
const outerHeight = 100;
const aspectRatio = 16/9;
let innerWidth, innerHeight, innerTop, innerLeft;
if (outerWidth / outerHeight > aspectRatio) {
innerWidth = outerHeight * aspectRatio;
innerHeight = outerHeight;
innerLeft = (outerWidth - innerWidth) / 2;
innerTop = 0;
} else {
innerWidth = outerWidth;
innerHeight = outerWidth / aspectRatio;
innerLeft = 0;
innerTop = (outerHeight - innerHeight) / 2;
}
let outerBoxWidth = 200;
let outerBoxHeight = 100;
let maxInnerBoxWidth = ((outerBoxWidth / 16) | 0);
let maxInnerBoxHeight = ((outerBoxHeight / 9) | 0);
let widthLower = maxInnerBoxHeight > maxInnerBoxWidth;
if(widthLower){
let innerBoxHeight = 9 * maxInnerBoxWidth;
let innerBoxWidth = 17 * maxInnerBoxWidth;
}else{
let innerBoxHeight = 9 * maxInnerBoxHeight;
let innerBoxWidth = 17 * maxInnerBoxHeight;
}
Based on the answer from Ouroborus I came up with the following which proves it works.
const getBoxes = (outerWidth, outerHeight, innerAspectRatio) => {
const outer = { width: outerWidth, height: outerHeight, aspectRatio: outerWidth / outerHeight }
const inner = { width: null, height: null, aspectRatio: innerAspectRatio }
const pillarBoxed = outer.aspectRatio > inner.aspectRatio
inner.width = !pillarBoxed ? outer.width : outer.height * inner.aspectRatio
inner.height = pillarBoxed ? outer.height : outer.width / inner.aspectRatio
return { outer, inner }
}
const adjust = 40
const boxes1 = getBoxes(160 + adjust, 90, 16/9)
const boxes2 = getBoxes(160, 90 + adjust, 16/9)
// display purposes only
const displayBoxes = (boxes, outerId, innerId) => {
const outerEl = document.getElementById(outerId)
outerEl.style.width = boxes.outer.width + 'px'
outerEl.style.height = boxes.outer.height + 'px'
const innerEl = document.getElementById(innerId)
innerEl.style.width = boxes.inner.width + 'px'
innerEl.style.height = boxes.inner.height + 'px'
}
displayBoxes(boxes1, 'outer1', 'inner1')
displayBoxes(boxes2, 'outer2', 'inner2')
// console.log('boxes1', boxes1)
// console.log('boxes2', boxes2)
#outer1, #outer2 {
background-color: black;
display: inline-flex;
justify-content: center;
align-items: center;
margin: 10px;
}
#inner1, #inner2 {
background-color: red;
}
<div id="outer1">
<div id="inner1"></div>
</div>
<div id="outer2">
<div id="inner2"></div>
</div>
To make this easy to test, I made the outer box 160x90 BUT 40px wider (first example) and 40px taller (second example). Given that this works nicely with a 16/9 aspect ratio, there should be 20px left and right in the first example and 20px above and below in the second example. Which there is!
Also, the inner box should be 160x90 in both examples, which it is!
Proof that this code works as expected.
I'm trying to create a progress bar for a product card track so by any click of the user on the prev and next buttons (which would scroll back or forward) the progress bar would advance or backup.
here's the code I came up with. the problem is the first click doesn't show any result and the prev button acts like the next button for the first time. It's like the code is one step behind.
I'm very new to javaScript and I can't figure out how this could happen.
const productScroll = () => {
rightButton.onclick = function () {
let scrollLeft = document.querySelector('#ProductSlider').scrollLeft;
let scrollPercent = ((scrollLeft - 0) / (5033 - 0)) * (100 - 0) + 0;
document.querySelector('div.progress-bar').style.width = `${scrollPercent}%`;
};
leftButton.onclick = function () {
let scrollLeft = document.querySelector('#ProductSlider').scrollLeft;
let scrollPercent = ((scrollLeft - 0) / (5033 - 0)) * (100 - 0) + 0;
document.querySelector('div.progress-bar').style.width = `${scrollPercent}%`;
};
If youre always 1 step behind, it could be that your percentage calculation is wrong. For example, if you have 5 steps and want to show progress for each step, starting at 1 and ending at 5, your progress bar needs to have 4 steps instead:
1 > 2 > 3 > 4 > 5 = 4 steps (total - 1)
In percentages, it looks like this for a 5 step progress bar:
1: 0%
2: 25%
3: 50%
4: 75%
5: 100%
Notice each increase is 25% (1/4) and not 20% (1/5).
So in abstract shape, your calculation would need to be:
((scroll / max) * (steps - 1)) / (steps - 1) * 100%
Which means your scrollLeft / 5033 needs to be between 0 and 4, divided by 4, then turned into a percentage:
const percentage = ((scrollLeft / 5033) * 4) / 4 * 100;
To create a progress bar, first create two “div” tag elements named id Progress_Status and myprogressbar.
To add a numeric label to indicate how far the user is in the process, the element inside or outside the progress bar is required which will display the progress status which in this case is myprogressbar.
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
</div>
Adding CSS:
#Progress_Status {
width: 50%;
background-color: #ddd;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
}
Adding JavaScript:
Next, Code below creates a dynamic progress bar(animated) using JavaScript functions update and scene.
function update() {
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 100) {
clearInterval(identity);
} else {
width++;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
Firstly, you fetch the element by id and then set starting width to 1. For the purpose of working example, i used setintervel to show the progression of progress bar.
All we are doing here is calling the scene function which checks if width is less than 100. If yes, then stop loader by clearing the interval. If not, then increment the width and add it to the with and progress label div for showing the progress in percentenge.
Complete Code:
function update() {
var element = document.getElementById("myprogressBar");
var width = 1;
var identity = setInterval(scene, 10);
function scene() {
if (width >= 100) {
clearInterval(identity);
} else {
width++;
element.style.width = width + '%';
element.innerHTML = width * 1 + '%';
}
}
}
#Progress_Status {
width: 50%;
background-color: #ddd;
}
#myprogressBar {
width: 1%;
height: 35px;
background-color: #4CAF50;
text-align: center;
line-height: 32px;
color: black;
}
<!DOCTYPE html>
<html>
<body>
<h3>Example of Progress Bar Using JavaScript</h3>
<p>Download Status of a File:</p>
<div id="Progress_Status">
<div id="myprogressBar">1%</div>
</div>
<br>
<button onclick="update()">Start Download</button>
</body>
</html>
I was playing around with JavaScript/canvas and I want my objects color to depend on the distance to its center from current mouse position.This is my current function that gets color every mousemove event:
function getColorFromDistance(node1,node2){
var dist = getDist(node1,node2); //Getting distance;
var cl = (Math.round(255/dist*255)).toString(16); //this needs to be a propper formula
return "#" + cl + cl + cl; //converting to hex
}
Currently I get a blink effect when the distance gets 255.
I need a way to get the colors strength be depended on the distance, so that the further mouse is away from object the more its darken and when mouse is on the objects center its fully white.Well you get the idea.I just need the formula
The formula would be calculate the distance between the two points and get a percentage based on the maximum value (width of canvas/window)
//this would need to be recalulated on resize, but not doing it for demo
var targetElem = document.querySelector("div.x span");
box = targetElem.getBoundingClientRect(),
x = box.left + box.width/2,
y = box.top + box.height/2,
winBox = document.body.getBoundingClientRect(),
maxD = Math.sqrt(Math.pow(winBox.width/2, 2) + Math.pow(winBox.height/2, 2));
document.body.addEventListener("mousemove", function (evt) {
var diffX = Math.abs(evt.pageX-x),
diffY = Math.abs(evt.pageY-y),
distC = Math.sqrt(Math.pow(diffX, 2) + Math.pow(diffY, 2)),
strength = Math.ceil(255 - (distC/maxD*255)).toString(16),
color = "#" + strength + strength + strength;
targetElem.style.backgroundColor = color;
});
html, body { height: 100%; }
div.x { position: absolute; top: 50%; left:50%; }
span { display: inline-block; width: 20px; height: 20px; border-radius: 50%; border: 1px solid black; overflow: hidden; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<p>Test</p>
<div class="x"><span> </span></div>
I am trying to slide image from left to right and after a set point it should again slide in reverse direction. This is my code somehow its not working as i am going wrong somewhere in the if statement.
(function($) {
var x = 0;
var y = 0;
//cache a reference to the banner
var banner = $("#banner");
// set initial banner background position
banner.css('backgroundPosition', x + 'px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
x++;
//x--;
//if you need to scroll image horizontally -
// uncomment x and comment y
}, 90);
if ($(banner.offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
})(jQuery);
div#banner {
width: 960px;
height: 200px;
margin: auto;
background: url(http://cdn-careers.sstatic.net/careers/gethired/img/companypageadfallback-leaderboard-2.png?v=59b591051ad7) no-repeat 0 0;
}
div#banner p {
font: 15px Arial, Helvetica, sans-serif;
color: white;
position: relative;
left: 20px;
top: 120px;
width: 305px;
padding: 20px;
background: black;
text-align: center;
text-transform: uppercase;
letter-spacing: 20px;
zoom: 1;
filter: alpha(opacity=50);
opacity: 0.5;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="banner"></div>
Firstly, you are using a IIFE (Immediately Invoked Function Expression) instead of a DOM ready handler. This code will only work if placed after the elements it references.
Use this shortcut for DOM ready that also provides a locally scoped $
jQuery(function ($) {...});
You also have a missing closing paren (or really a redundant $( as it is already a jQuery object):
JSFiddle: http://jsfiddle.net/g0gn4osy/7/
You also need to have a delta value that changes the direction when you hit a bound value. I sped up your timing to show this:
jQuery(function ($) {
var delta = 1;
var y = 0;
//cache a reference to the banner
var $banner = $("#banner");
// set initial banner background position
$banner.css('background-position', '0px' + ' ' + y + 'px');
// scroll up background position every 90 milliseconds
window.setInterval(function () {
var position = parseInt($banner.css('background-position'));
if (position >= 40 || position < 0) {
delta = -delta;
}
position += delta;
$banner.css("background-position", position + 'px' + ' ' + y + 'px');
}, 10);
});
Notes:
You also had backgroundPosition instead of background-position for the CSS property. I prefer to use the values that match the css properties (personal choice only for maintenance).
To avoid the redundant $() issue, I recommend you prefix jQuery variables with $. e.g. $banner in this case. Then it becomes obvious you are dealing with a jQuery object.
I tend to use the current position of an element, rather than keep a global var running. This allows for external influences to change the position and still work. Have removed x and just use position.
Inspired and modelled on Gone Coding's answer.
I have expanded his example to take into account the image width and the view pane DIV width.
It now scrolls to image end and then back. You never scroll off the canvas or past a visible part of the image. It doesn't jerk or rock, just switches direction.
With awareness of the viewing box width you can easily adjust the width of div#banner to fit the display space and the code adjusts. Just remember to set the background image width imgW var.
I have also added:
Visual indicator for testing with a current position and scroll direction. (With -1 is scrolling left, +1 is scrolling right),
Image start position in px. (A minus number or Zero. With 0 is start image at left, Minus number is start image part way through i.e image pre-scrolled left)
Image start vertical position in px (to vertically pull image up/down. Useful if view pane height shorter than image height and you want to tweak what is seen)
Things to do:
Change image URL (background: url(IMAGENAMEHERE) no-repeat 0 0;)
Insert image width (var imgW = #PIXELWIDTH#;)
Play with WIDTH: and HEIGHT: of view pane (div#banner)
Enjoy.
Fiddle
Have a play http://jsfiddle.net/Lm5yk46h/
Image credit Mark Higgins | Dreamstime.com Image source for purchase
Javascript
jQuery(function ($) {
var delta = 1;
var imgW = 3000;//width of image px
var imgY = 0;//to shift image view vertically px (Minus or zero)
//cache ref to #banner
var $banner = $("#banner");
var viewpaneW = $banner.width();
var endpos = (imgW - viewpaneW);
var startpos = 0;//0 or negative number
// set initial banner background position
$banner.css('background-position', startpos + 'px' + ' ' + imgY + 'px');
// scroll background position every 20ms
window.setInterval(function () {
var position = parseInt($banner.css('background-position'));
// minus is left, plus is right
if (position >= 0 ) delta = -delta;//go left
if (position < (-1*endpos)) delta = (-1*delta);//go right
position += delta;//increment posn
$banner.css("background-position", position + 'px' + ' ' + imgY + 'px');
$("#indicator").text('Posn:' + position + ' | direction: ' + delta);
}, 20);
});
CSS
div#canvas {
background-color: #999;
width: 100%;
height: 400px;
margin:0;padding:10px;
}
div#banner {
width: 460px;
height: 300px;
margin: 10px;
background: url(https://digido.net/eg/newcastle-beach-3000x300.jpg) no-repeat 0 0;
}
div#banner p {
font: 13px Arial, Helvetica, sans-serif;
color: white;
position: relative;
left: 0;
top: 310px;
width: 99%;
padding: 10px;
background: black;
text-align: center;
text-transform: uppercase;
letter-spacing: 8px;
zoom: 1;
filter: alpha(opacity=50);
opacity: 0.5;
}
HTML
<div id="canvas">
<div id="banner">
<p id="indicator">Hit run</p>
</div>
</div>
Just put the if condition inside the setInterval. And check the syntax error. The if doesn't have a closing }:
// scroll up background position every 90 milliseconds
window.setInterval(function() {
banner.css("backgroundPosition", x + 'px' + ' ' + y + 'px');
x++;
//x--;
if (banner.offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
}, 90);
Your "if" should be like this:
if ($(banner).offset().left > 40) {
banner.css("backgroundPosition", "0px 0px ");
}
https://jsfiddle.net/wc4b2g97/
your if should be inserted inside your setInterval handler, so it would get evaluated every 90 milliseconds (thank you for correcting me).
Actually, your if is evaluted only the first time, when your javascript file is parse.
Add it into your setInterval and it should work as expected
I want to create a rectangle on mousedown that drags across a grid and remains there on mouseup, snapping to the gridlines and outputting the coordinates for top left and bottom right of the it's position (x1,x2,y1,y2). Any help on starting to build this would be much appreciated.
I have a 500x500 grid with squares of 10x10 (example - jsFiddle).
Grid Code:
function creategrid(size){
var standardW = Math.floor((500) / size),
standardH = Math.floor((500) / size);
var standard = document.createElement('div');
standard.className = 'grid';
standard.style.width = (standardW * size) + 'px';
standard.style.height = (standardH * size) + 'px';
for (var i = 0; i < standardH; i++) {
for (var p = 0; p < standardW; p++) {
var cell = document.createElement('div');
cell.style.height = (size - 1) + 'px';
cell.style.width = (size - 1) + 'px';
cell.style.position = 'relative'
cell.style.zIndex= '2';
standard.appendChild(cell);
}
}
document.body.appendChild(standard);
}
creategrid(10);
CSS for grid:
.grid {
margin: 0px auto auto;
border: 1px solid #000;
border-width: 0 1px 1px 0;
background-color: #CCC;
}
.grid div {
border: 1px solid #000;
border-width: 1px 0 0 1px;
float: left;
}
#tooltip {
text-align:center;
background:black;
color:white;
padding:3px 0;
width:150px;
position:fixed;
display:none;
white-space:nowrap;
z-index:3;
}
I've found some snapping code through google http://jqueryui.com/draggable/#snap-to but I am literally stuck (I'm a complete beginner at JQuery).
Alternatively if anyone has a better idea of how to do this then that would be more than welcome.
Some background if you want to suggest a different way to do it: This is for a website running off of an SQL server built in python and django. The data it outputs are jSON objects but otherwise I'm just using html, css and javacript/jQuery for the front end. -- Not sure if that info is useful or not.
EDIT added code for mouseover grid coordinates in jQuery
$(window).load(function() {
var tooltip = $('<div id="tooltip">').appendTo('body')[0];
$('.coords').
each(function() {
var pos = $(this).offset(),
top = pos.top,
left = pos.left,
width = $(this).width(),
height = $(this).height();
$(this).
mousemove(function(e) {
var x = ((e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft) - left).toFixed(0),
y = (((e.clientY + document.body.scrollTop + document.documentElement.scrollTop) - top)).toFixed(0);
$(tooltip).text( x + ', ' + y).css({
left: e.clientX + 20,
top: e.clientY + 10
}).show();
}).
mouseleave(function() {
$(tooltip).hide();
});
});
});
If i understood your question correctly, you don't really need jQueryUI for that.
You need to find mouse position snapped to the cell of the grid on mousemove and resize your selection rectangle.
function getMousePos (e) {
return {
'left': Math.floor((e.pageX - gridOffset.left) / cellSpacing) * cellSpacing,
'top': Math.floor((e.pageY - gridOffset.top) / cellSpacing) * cellSpacing
}
}
Here is an example - http://jsfiddle.net/4efTV/
I recommend you to use that plugin, jQuery UI, its really simple to use take a look at this fiddle: http://jsfiddle.net/promatik/hBQxb/
HTML
<div class="snap-box">snap box</div>
Javascript:
$( ".snap-box" ).draggable({ grid: [ 10,10 ] });
CSS:
.snap-box {
width: 50px;
height: 50px;
position: absolute;
z-index: 10;
}