I am creating a sortable drag and drop piece where users can sort and reorder the grid items as they like. I am placing the items with position absolute to get the animation correct.
Basically, I have a container and there will be n number of elements in a row and I need to place these items at equal distances from each other.
I need to find a reliable way to calculate the left values of each element so that it can be placed correctly.
The following is my current code:
let rowElemsCount= 4;
const arrangeItems = (items,rowElemsCount,container,elemWidth,elemHight) => {
elemHight= elemHight|| elemWidth;
let containerWidth = container.offsetWidth;
let containerHeight = Math.ceil(items.length / rowElemsCount) * elemHight;
container.style.height = containerHeight + "px";
items.forEach((item,i)=>{
let pos = {
x: (i%rowElemsCount) * elemWidth+ "px",
y: Math.floor(i/rowElemsCount) * elemHight+ "px"
};
item.style.cssText = "top:"+pos.y+";left:"+pos.x+";"
item.dataset.index = i;
});
}
I use this method to organize the elements, the calculation for the top is perfect but for the left, it just places the elements one after the other. I need to someway replicate the justify-content: space-between for this.
You're forgetting to calculate the white space between your elements in your calculations. If you add up the width of all the children's containers, then subtract it from the width of the parent you get the leftover whitespace you need to break into fractions.
parent width - children total width = leftover whitespace
if the white space is to only be divided into portions between the children elements, you should then divide that leftover whitespace by children count -1 because the first element doesn't need padding
leftover whitespace / ( children - 1 ) = space between
now your left is just that leftover white plus the left value
item index * ( element width + white space )
Now they should be spaced correctly. You're very close with your current code, and implementing these changes should be pretty easy!
Related
So what I want to happen is that when viewing the Span the text is normal but as you scroll down it starts moving until it looks like such:
Before the effect:
While the effect occurs:
The header is represented by spans for each letter. In the initial state, the top pixel value for each is 0. But the idea as mentioned is that that changes alongside the scroll value.
I wanted to keep track of the scroll position through JS and jQuery and then change the pixel value as needed. But that's what I have been having trouble with. Also making it smooth has been another issue.
Use the mathematical functions sine and cosine, for characters at even and odd indices respectively, as the graphs of the functions move up and down like waves. This will create a smooth effect:
cos(x) == 1 - sin(x), so in a sense, each character will be the "opposite" of the next one to create that scattered look:
function makeContainerWiggleOnScroll(container, speed = 0.01, distance = 4) {
let wiggle = function() {
// y-axis scroll value
var y = window.pageYOffset || document.body.scrollTop;
// make div pseudo-(position:fixed), because setting the position to fixed makes the letters overlap
container.style.marginTop = y + 'px';
for (var i = 0; i < container.children.length; i++) {
var span = container.children[i];
// margin-top = { amplitude of the sine/cosine function (to make it always positive) } + { the sine/cosine function (to make it move up and down }
// cos(x) = 1 - sin(x)
var trigFunc = i % 2 ? Math.cos : Math.sin;
span.style.marginTop = distance + distance * trigFunc(speed * y)/2 + 'px';
}
};
window.addEventListener('scroll', wiggle);
wiggle(); // init
}
makeContainerWiggleOnScroll(document.querySelector('h2'));
body {
height: 500px;
margin-top: 0;
}
span {
display: inline-block;
vertical-align: top;
}
<h2>
<span>H</span><span>e</span><span>a</span><span>d</span><span>e</span><span>r</span>
</h2>
Important styling note: the spans' display must be set to inline-block, so that margin-top works.
Something like this will be the core of your JS functionality:
window.addEventListener('scroll', function(e) {
var scrl = window.scrollY
// Changing the position of elements that we want to go up
document.querySelectorAll('.up').forEach(function(el){
el.style.top = - scrl/30 +'px';
});
// Changing the position of elements that we want to go down
document.querySelectorAll('.down').forEach(function(el){
el.style.top = scrl/30 +'px';
});
});
We're basically listening in on the scroll event, checking how much has the user scrolled and then act upon it by offsetting our spans (which i've classed as up & down)
JSBin Example
Something you can improve on yourself would be making sure that the letters wont go off the page when the user scrolls a lot.
You can do this with simple math calculation, taking in consideration the window's total height and using the current scrollY as a multiplier.
- As RokoC has pointed out there is room for performance improvements.Implement some debouncing or other kinds of limiters
I'm building a Pinterest type board plugin with jQuery, where I'm having difficulty figuring out how they position their modules.
Below is a snippet of how each element is being placed per their nth-value in the HTML. But this isn't right because Pinterest positions elements that go to the next row underneath the shortest column. Quite like this pen here, http://codepen.io/nikhilkumar80/pen/oxpXVK, but I found it difficult to understand.
function modPosition() {
$this.find(".pinterest-board__mod").each(function( modIndex ) {
$(this).css({
position: "absolute",
left: columnWidth * (modIndex % settings.columns) + "%",
width: columnWidth + "%"
});
// ..........
});
}
modPosition();
Here's my CodePen link, http://codepen.io/joshuawaheed/pen/beeJKq?editors=0010
I'm also having difficulties figuring out how to set the elements top position.
What can I do to make this work? I've put this in a function so that the positioning can run it again on document resize and when a user clicks on a filter option to remove the elements and append the appropriate modules from the appropriate array. The plugin is also set to determine module widths based on the options columns value.
Thank you in advance.
You can implement it in several ways
I suggest you two ways
1) you can use one of the js modules
You can read more about it there
https://designshack.net/articles/css/masonry/
http://www.wtfdiary.com/2012/08/6-amazing-jquery-plugins-to-design.html
2) You can use css rules(flexbox)
You can read more about it there
https://css-tricks.com/snippets/css/a-guide-to-flexbox/
Both of these methods have positive and negative traits
For example fleksboks is not supported by all versions of the browser
But JS is more load the processor
I've got it working. I solved this by:
Creating an array, its lengt equal to the column count.
Storing the height value of modules in the first row in the array.
Looping through each module.
Injecting the smallest array value as css position top for the current module.
Adding the height of the current module with the array item containing the smallest value.
Setting the position left value by dividing 100% with the index of of the smallest value in the array.
Here is the code I wrote, which you can view and fork by following this link
function modPosition() {
var columnsHeight = [/* Length equal to column count */], // This will be recreated on window resize.
columnCount = /* Column count value */;
/* Set CSS position top and left. */
function modCssTopLeft() {
var columnIndex = 0;
$this.find(".pinterest-board__mod").each(function( modIndex ) {
var topPos = 0,
leftPos = 0;
if ( modIndex >= columnCount) {
topPos = Math.min.apply( Math, columnsHeight ); // Get smallest value in array.
leftPos = 100 * columnsHeight.indexOf(topPos) / columnCount; // Set left position based on column count.
columnsHeight[columnsHeight.indexOf(topPos)] += $(this).outerHeight(); // Change arrays smallest value by adding it with current modules height.
}
else {
leftPos = 100 * (modIndex++) / columnCount; // Positioning for the modules in the first row.
}
$(this).css({
position: "absolute",
top: topPos + "px",
left: leftPos + "%"
});
$(this).closest(".pinterest-board__content").css({
height: Math.max.apply( Math, columnsHeight ) // Set height to the modules parent container.
});
});
}
modCssTopLeft();
}
modPosition();
$(window).resize(function() {
modPosition();
});
I have a page that dynamically loads a google timeline chart onto a div. The timeline increases in height as you add items on to it. If the chart is smaller than the div, it will be visible with empty space at the bottom. If the chart is larger, it will show scrollbars on the side.
What I would like is for the container div to always show the full chart or, another way, I would like the container div height to dynamically match the height of the chart.
I have tried to approximate the average size of each chart line and then adjust the div height when I load the chart with:
$("#gantt_chart").height(data['num_contents']*46);
But, although that somehow works, it's far from perfect because the approximation on height starts to accumulate with each additional line and the empty space at the bottom of the div increases which is not an elegant solution at all.
How would I ensure that the container div always shows the full chart?
I hope this is clear enough.
Many thanks.
The chart determines its height by either checking the height option passed into the draw call, or by checking the container height if the height option is not specified. I would suggest using the option instead of changing the div height via jQuery. I find the best method for dynamically calculating the chart height is to take the height of a row (typically 41px) times the number of rows, plus some padding for the top and bottom:
var height = data.getNumberOfRows() * 41 + 30;
Though in the doc they have shown to adjust height of div from where we are specifying the id of timeline but I recommend you to have set the height from the internal method.
// set the height for padding(padding height)
var pH = 40;
// get total height of rows (row height)
var rH = dataTable.getNumberOfRows() * 15;
// total chart height (chart height)
var cH = rH + pH;
// Now set this chart height to the timeline via option object
// apart from height, other attributes are just optional
var options = {
height: chartHeight,
colors: ['#339900', '#e6e600', '#B00000'],
tooltip: {
isHtml: true
},
timeline: {
colorByRowLabel: false
},
avoidOverlappingGridLines: true
};
Been trying to do the same thing myself. The top answer wasn’t exactly right and caused an inner scroll bar to appear at times.
Here’s what worked for me:
var barLabelFontSize = 12; //default font size for bar labels
try{barLabelFontSize = options.timeline.barLabelStyle.fontSize; //bar height is dependent on the bar label's font size that you set...
}catch(e){}
var barHeight = barLabelFontSize * 1.196; //found in google api as: this.PU = 1.916 * this.UF.fontSize;
var barMargin = barLabelFontSize * 0.75; //found in google api as: this.Rfa = .75 * this.UF.fontSize;
var rowHeight = barHeight + barMargin * 2; //found in google api as: b.height = 2 * this.Rfa + this.PU (+additional complication for grouped bars, if anyone wants to try work that out it was: b.height = 2 * this.Rfa + this.PU * a.SI.length + this.Qfa * (a.SI.length - 1); (where Qfa is 0.583 * font size, and a.SI.length is the number of grouped bars displayed on the row)
var chartAreaHeight = rowHeight * dataTable.getNumberOfRows();
var axisHeight = 24; //worked out by querying: document.getElementById('timelineChart') getElementsByTagName('svg')[0].getBBox().height;
var whiteSpaceHeight = 28; //trail-and-error to find how much whitespace was required to stop a scroll bar appearing, 27 works too, but I rounded up to play it safe
var chartHeight = chartAreaHeight + axisHeight + whiteSpaceHeight;
You can also reverse engineer this to set the height of the bars using the bar label font size.
I have a site. I want to make 3 vertical divs with equal height. For this purposes I change the height of last block in each column/div.
For example, the naming of 3 columns are:
.leftCenter
.rightCenter
.right
Now I wrote a code which set the equal height for .leftCenter and .rightCenter:
var left = $('.leftCenter').height();
var center = $('.rightCenter').height();
var news = $('#newItemsList').height();
if (center < left)
$('.rightCenter').height(center + (left-center));
else if (center > left)
$('#newItemsList').height(news + (center-left));
news is the latest subblock in left column (there are 3 images in it). So, if central div is bigger than left div, I change the height of news to make them equal. This code works in Firefox, but doesn't work in Chrome. That's the first question. And the last is: how to make equal 3 divs (including right one).
I needed to make elements equal in height and width so I made the following function that allows you to define a height, or width, or really whatever at it. refType would be used if you sent a min-height and needed it to match the height of the tallest element.
elemsEqual = function (options) {
var defaults = {
'type' : 'height',
'refType' : '',
'elements' : [],
'maxLen' : 450
},
settings = $.extend({}, defaults, options),
max = 0;
$(settings.elements.join(",")).each(function () {
max = Math.max( max, parseInt( $(this).css(settings.type) ) );
if(settings.refType.length) max = Math.max( max, parseInt( $(this).css(settings.refType) ) );
});
max = ((max < settings.maxLen) ? max : settings.maxLen);
$(settings.elements.join(",")).css(settings.type, max + "px");
};
elemsEqual({elements : ['#selector1','#selector2', ... '#selectorN'], type : 'height'});
Well I have this so far:
//Get the height of the right column since it starts at a different Y position than the other two
var right=$('.right').outerHeight(1)-$('.left').children('header').outerHeight(1)-$('.left .innav').outerHeight(1);
//Get the max of the 3
var height_max=Math.max($('.leftCenter').outerHeight(1),$('.rightCenter').outerHeight(), right);
//Apply the max to all 3
$('.rightCenter').height(height_max-3); //-3 to accommodate for padding/margin
$('.right').height(height_max);
$('.leftCenter').height(height_max);
The only problem is that it does not make #newItemsList as tall as the parent, .leftCenter. It also assumes that the right div will be largest, so I don't know if it will still work if it isn't the biggest of the 3.
Tracing this jquery autocomplete function, can someone explain in detail what is going on here?
function showResults() {
// get the position of the input field right now (in case the DOM is shifted)
var pos = findPos(input);
// either use the specified width, or autocalculate based on form element
var iWidth = (options.width > 0) ? options.width : $input.width();
// reposition
$results.css({
width: parseInt(iWidth) + "px",
top: (pos.y + input.offsetHeight) + "px",
left: pos.x + "px"
}).show();
};
It uses this function:
function findPos(obj) {
var curleft = obj.offsetLeft || 0;
var curtop = obj.offsetTop || 0;
while (obj = obj.offsetParent) {
curleft += obj.offsetLeft
curtop += obj.offsetTop
}
return {x:curleft,y:curtop};
}
Reference: http://www.pengoworks.com/workshop/jquery/lib/jquery.autocomplete.js
offsetLeft and offsetTop are properties that describe how many pixels obj is offset from it's containing element. What this function does is:
Compute the offset of obj from its parent element and save these values in variables
Set obj to be the parent element of the item last computed from
Goto 1. Repeat until you have reached the top level of the DOM.
This calculates how many pixels that obj is from the top and left sides of the rendered page.
Basically, it's figuring out the X and Y coordinates (left and top in CSS terms) of the input field you're using autocomplete on and setting the top and left CSS attributes of the autocomplete HTML to have it appear there. In other words, it's matching up the corners of the input Element and autocomplete layer so they appear at the same place (it's doing the same with widths and heights, too).
In the findPos function, we're basically walking back up the DOM tree getting the offsets (see Mozilla's dev center) of each Element from their parent (and eventually the body tag) to get the precise x and y coordinates of that input so that we can position the autocomplete layer at it's coordinates. We sum these, and wind up with the x and y values we pass back up to use in setting the left and top positions in CSS.
It's essentially copying the x and y position, height, and width of your input and applying them to the autocomplete layer so that they match up visually.