Find position of element in jquery using .position() not working - javascript

I'm doing a simple animation by click a box to move to the right by 100px on each click. And I want that when its position reaches 400, it should move to the left by 100px on each click.
I did a console.log() but the console only show the initial positioning of the element, and it doesn't update after each click.
I also want the final position to show on the <span id="info"> but it also doesn't update.
Why is that?
Many Thanks
jquery:
$('#somebox > a').click(function(e){
var thisElem = $(this),
aPos = thisElem.position();
thisElem.animate({ marginLeft: '+=100px' });
if(aPos.left === 400){
thisElem.animate({ marginLeft: '-=100px' });
}
console.log('finish pos: ' + aPos.left);
$('#info').text(aPos.left + 'px');
e.preventDefault();
});
HTML:
<div id="somebox">
show
<span id="info">10px</span>
</div>
CSS:
#somebox{
position: relative;
background: #EEF0EB;
border: 1px solid #dedbd7;
height: 300px;
width: 500px;
}
#somebox a{
display: block;
color: #fff;
font-weight: bold;
border: 1px solid #099;
background: #0C9;
padding: 5px;
width: 50px;
text-decoration: none;
}
#info{
display: block;
font-size: 36px;
color: #777;
font-weight: bold;
width: 100px;
margin: 100px auto;
}

How about using the .data() method to make your life easier?
$('#somebox > a').click(function(e){
var $this = $(this);
var rel = $this.data('rel') || '+=';
var step = 100, // amount to move each click
min = 0, // minimum left movement
max = 400; // maximum right movement
$this.animate({ marginLeft: rel + step + 'px' },function(){
var margin = parseInt($this.css('margin-left'),10);
if (margin <= min || margin >= max){
$this.data('rel',(rel == '+=' ? '-=' : '+='));
}
});
});
DEMO

Add this to your style sheet:
#somebox a {
position: relative;
}
And on your jQuery where it says marginLeft, change it to just left. Do some more tweaking afterwards to get it to show the proper value.

Related

Customizing jQuery scrolling effect

I'm working on a website where I have made a simple scrolling effect using jQuery. Basically, I just have the page scroll to a specified section when the user clicks on one of the navigation links. I would like to customize this so that the header (which I'm using as my scrolling target) is not at the very top of the page. I want it more in the middle. Does anybody know how I can easily customize this? Below is my jQuery code.
jQuery Scroll effect
$('a[href*="#"]').on('click', function(e) {
e.preventDefault()
$('html, body').animate(
{
scrollTop: $($(this).attr('href')).offset().top,
},
500,
'linear'
)
});
Thank you!
The key is to calculate the offset, Try this:
$('a[href*="#"]').on('click', function(e) {
e.preventDefault()
var id = $(this).attr('href');
var $element = $(id);
var elementHeight = $element.height();
var winHeight = $(window).height();
var offset;
if(elementHeight >= winHeight) //if element height > window height, just put the element to top place.
{
offset = 0;
}
else // else make it to the middle place of window.
{
offset = Math.round((elementHeight - winHeight) / 2);
}
$('html, body').animate(
{
scrollTop: $element.offset().top + offset,
},
500,
'linear'
)
});
body, html {
padding: 0;
margin: 0;
}
.nav-wrap {
width: 100vw;
position: fixed;
background: rgba(0,0,0,0.5);
padding: 10px;
}
.nav-wrap > a {
color: #ffffff !important;
padding: 10px;
}
section {
display: block;
width: 100vw;
}
#id1 {
background: #ff0000;
height: 50vh;
}
#id2 {
background: #00ff00;
height: 80vh;
}
#id3 {
background: #ffff00;
height: 120vh;
}
#id4 {
background: #0000ff;
height: 30vh;
}
#id5 {
background: #000000;
height: 60vh;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="nav-wrap">
ID1
ID2
ID3
ID4
ID5
</div>
<div class="section-wrap">
<section id="id1"></section>
<section id="id2"></section>
<section id="id3"></section>
<section id="id4"></section>
<section id="id5"></section>
</div>

Dynamic transform style property while scrolling

I'm in a blind spot with my small jQuery script.
The point is that I'm trying to make an element to rotate, and to apply the rotation value dynamically as the user is scrolling through the page.
It works here on stackoverflow but I can't get this to work on my website...
The only external library I'm using is JQuery.
Can you please tell me where is the problem?
var $animObject = $('.animateObject');
var $window = $(window);
$window.on('scroll', function() {
var fromTop = $window.scrollTop() / -4;
$animObject.css('transform', 'rotate(' + fromTop + 'deg)')
});
.header {
width: 100%;
height: 100vh;
background-image: url('https://simply-design.ml/dev/img/start1.jpg');
display: flex;
justify-content: center;
align-items: center;
}
.header-content {
padding: 30px;
max-width: 470px;
}
.header-wrapper {
padding: 50px;
border: solid 3px #fff;
}
.header h1 {
font-size: 30px;
color: #fff;
text-align: center;
}
.header p {
font-size: 20px;
color: #fff;
text-align: center;
}
.p-title {
font-size: 14px;
color: #fff;
}
.head-button {
padding: 10px 25px;
background-color: #3b88df;
color: #fff;
font-size: 20px;
cursor: pointer;
font-family: 'Source Sans Pro', sans-serif;
}
.head-button:hover {
background-color: #2c78ce;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<header class="header">
<div class="header-content">
<center>
<div class="header-wrapper animateObject">
<h1>title</h1>
<div style="height: 2px; width: 70px; background-color: #fff; margin: 20px;"></div>
<p>subtitle</p>
</div>
</center>
</div>
</header>
<div style="height: 1000px"></div>
Check this example I've made without jQuery, which shows how to rotate an element based on the scroll position of the window, but only once the element is in view.
I've decided to do this without jQuery because it's better for performance, working directly with the DOM instead of passing through jQuery, and also because it's relatively simple code, understandable.
Find out how much was scrolled
Get the target's element absolute position
Calculate if the element is within the viewport (if not, break)
If it's in, save the scroll value at that point
Subtract that value from the current scroll value to get the value from that point on
Use the new value as baseline for the transformation
var elm = document.querySelector('b');
var onScroll = (function(){
var startPos;
function run(){
var fromTop = window.pageYOffset,
rect = elm.getBoundingClientRect(),
scrollDelta;
// check if element is in viewport
if( (rect.top - window.innerHeight) <= 0 && rect.bottom > 0 )
startPos = startPos === undefined ? fromTop : startPos;
else{
startPos = 0;
return;
}
scrollDelta = (fromTop - startPos) * 1; // "speed" per scrolled frame
elm.style.transform = `translateX(${scrollDelta}px) rotate(${scrollDelta}deg)`;
console.clear();
console.log(scrollDelta);
}
run();
return run;
})()
window.addEventListener('scroll', onScroll);
html, body{ height:100%; }
body{ height:1500px; }
b{
position:fixed;
top: 20px;
left:20px;
width:100px;
height:100px;
background:red;
}
<b></b>
inspect the <b> element while scrolling and see that it only gets transform when it is in view.

How to let a div become visible when the bar is full [JS]

my name is Daniel and i'm making a drinking game for school, I want to let a div to become visible when the bar is full (so you know when the bar is full and you win the game), but i have no idea how to do this...
Could you help me out?
HTML:
<div class="col-xs-12" style="display: none;" id="hiddenText">
<div id="bar" class="animated bounceInUp">
</div>
</div>
CCS:
#bar {
background-color: #F8F8F8 ;
width: 340px;
height: 24px;
margin-right: auto;
margin-left: auto;
}
#bar > div {
margin-top: 30px;
max-width: 334px;
width: 100%;
height: 16px;
background: #9d3349;
position: relative;
top: 4px;
left: 3px;
transition: width 500ms;
}
JS:
var jumpsize = 2.77, // %
body = $("body");
(container = $("#bar")), (bar = container.children("div")), (topcnt = function(
px
) {
return 100 * px / container.width();
}), (set = function(pcnt) {
bar.css({ width: pcnt + "%" });
});
body
.on("click", ".card1, .card2, .card3, .card4", function() {
set(topcnt(bar.width()) + jumpsize);
});
set(0);
The reason its not working is because u forgot to put the if statement in the function u run on click. So the if statement only runs once. and on first load it will result in false. To fix your code move the if statement in your Body.onclick.
Next time it would be smart to include the full javascript that is relative to the function.
By looking at the online code i was able to find the issue.
Hope this resolves your issues.
~Yannick
When you hit your target you need to remove the CSS styling of Display = none.
W3 schools page here for some helpful info to help you learn some more.
https://www.w3schools.com/jsref/prop_style_display.asp
The line below inserted when you reach your goal to display should make the bar appear.
document.getElementById("hiddenText").style.display = "block";
I'm not sure you want this, but try this:
var jumpsize = 2.77, // %
width = 0,
body = $("body");
(container = $("#bar")), (bar = container.children("div")), (topcnt = function(
px
) {
return 100 * px / container.width();
}), (set = function(pcnt) {
bar.css({ width: pcnt + "%" });
if(pcnt >= 100) {$('#hiddenText').show();}
});
body
.on("click", ".card1, .card2, .card3, .card4", function() {
width += jumpsize;
set(topcnt(width));
});
set(0);
#bar {
background-color: #F8F8F8 ;
width: 340px;
height: 24px;
margin-right: auto;
margin-left: auto;
}
#bar > div {
margin-top: 30px;
max-width: 334px;
width: 100%;
height: 16px;
background: #9d3349;
position: relative;
top: 4px;
left: 3px;
transition: width 500ms;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="col-xs-12" style="display: none;" id="hiddenText">
<div id="bar" class="animated bounceInUp">
<div></div>
</div>
</div>
<button class="card1">click me</button>
You are using jQuery so quicker will be:
$('#hiddenText').show();
Edit:
sooo
if($('#bar').children('div').width() >= 334){
$('#hiddenText').show();
}
As You can see the div with progress bar can have max od 334 px. Check if it has it and if yes then show the text. Put this in that click event
Seems to me like you're overcomplicating things a little bit with the percentage calculations. I would just add a variable for the width of the bar that starts at 0 and increase this with the jumpsize on every click. Once this new variable goes over or equals 100 you show the hidden div.
HTML
<div class="col-xs-12" id="hiddenText">
<div id="bar" class="animated bounceInUp">
<div></div>
</div>
</div>
<button id="button">Click me</button>
<div id="showOnComplete">Show me when the bar is full!</div>
CSS
#bar {
width: 340px;
height: 24px;
padding: 4px 3px;
margin: 30px auto 0;
background-color: #f8f8f8;
}
#bar > div {
position: relative;
float: left;
height: 100%;
width: 0;
max-width: 100%;
background: #9d3349;
transition: width 500ms;
}
#button {
margin: 20px auto;
display: block;
}
#showOnComplete {
width: 400px;
padding: 20px;
margin: 20px auto;
background: blue;
color: #fff;
text-align: center;
display: none;
}
JS
(function($) {
var jumpSize = 20, //increased this for the fiddle, so we don't have to click as often
barWidth = 0,
$bar,
$showOnComplete;
$(function() {
$bar = $("#bar").children("div");
$showOnComplete = $("#showOnComplete");
$(document).on("click", "#button", function() {
barWidth += jumpSize;
$bar.width(barWidth + "%");
if (barWidth >= 100) $showOnComplete.show(); //optionally add a setTimeout of 500 here to account for the final transition of the bar
});
});
})(jQuery);
I've made a fiddle for it here.

Move to specific div based on button click

I was trying to move the divs (here it's question number) based on the prev and next button. So that the selected question is always visible on screen.
Here is the demo : http://jsfiddle.net/arunslb123/trxe4n3u/12/
Screen :
click and question number and click prev or next button to understand my issue.
My code :
$("#next")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': scrollTo
}, 800);
});
});
$("#prev")
.click(function () {
$(".c.current-question")
.each(function () {
var divIdx = $(this)
.attr('id');
var scrollTo = $('#' + divIdx)
.position()
.left;
$("#scrollquestion")
.animate({
'scrollLeft': -scrollTo
}, 800);
});
});
Using scrollLeft is a bit tricky. I did a small redo of your use-case based on positioning and then moving it based on left of the container. The tricky part is to reliably calculate the negative position when scrolled to the extreme right. Also, need to take into account the widths and margins.
Check the below snippet:
var $wrap = $("#numWrap"), $strip = $("#strip"),
$leftArrow = $(".wrapper > .arrows").first(),
wrapWidth = $wrap.width() + $leftArrow.width(),
margin = 10;
fill(20); select($(".numberItem").first());
$strip.on("click", ".numberItem", function() { select($(this)); });
function select($elem) {
$(".numberItem").removeClass("selected");
$elem.addClass("visited").addClass("selected");
focus($elem[0]);
}
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
$(".wrapper").on("click", "a.arrow", function() {
var stripPos = $strip.position();
if (this.id == "lft") {
$strip.css({"left": stripPos.left + (wrapWidth / 2)});
} else {
$strip.css({"left": stripPos.left - (wrapWidth / 2)});
}
});
$(".controls").on("click", "a.arrow", function() {
var $sel = $(".selected"), numPos, $sel, elemWidth;
$elem = $sel.length > 0 ? $sel.first() : $(".numberItem").first();
if (this.id == "lft") {
$sel = $elem.prev().length > 0 ? $elem.prev() : $elem;
select($sel);
} else {
$sel = $elem.next().length > 0 ? $elem.next() : $elem;
select($sel);
}
numPos = $sel.offset(); elemWidth = $sel.width() + margin;
numRight = numPos.left + elemWidth;
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
});
function fill(num){
for (var i = 1; i <= num; i++) {
var $d = $("<a href='#' class='numberItem'>" + i + "</a>");
$strip.append($d);
}
}
* { box-sizing: border-box; padding: 0; margin: 0; font-family: sans-serif; }
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative;
}
div.strip {
position: absolute; left: 0px;
width: auto; white-space: nowrap;
transition: left 1s;
}
a.numberItem {
display: inline-block; text-align: center; margin: 0px 8px;
background-color: #fff; border-radius: 50%; width: 48px; height: 48px;
font-size: 1.2em; line-height: 48px; text-decoration: none;
}
a.numberItem.visited { background-color: #fff; color: #000; border: 2px solid #01aebc; }
a.numberItem.selected { background-color: #01aebc; color: #fff; }
div.controls { clear: both; }
div.controls > div.arrows { width: auto; margin: 0 12px; }
a, a:focus, a:active, a:link, a:visited {
display: inline-block;
text-decoration: none; font-weight: 600;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="wrapper">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈</a>
</div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">〉</a>
</div>
</div>
<div class="controls">
<div class="arrows">
<a id="lft" class="arrow" href="#">〈 Previous</a>
</div>
<div class="arrows">
<a id="rgt" class="arrow" href="#">Next 〉</a>
</div>
<div>
Explanation:
Using absolute positioning on the number container, which is nested to get 100% width.
Markup:
<div class="wrapper">
<div class="arrows"><a id="lft" class="arrow" href="#">〈</a></div>
<div id="numWrap" class="numWrap">
<div id="strip" class="strip"></div> <!-- nesting here -->
</div>
<div class="arrows"><a id="rgt" class="arrow" href="#">〉</a></div>
</div>
CSS:
div.wrapper {
background-color: #ddd; width: 100vw; height: 64px;
clear: both; overflow: hidden; margin-top: 16px;
}
div.arrows {
float: left; width: 10%; min-width: 24px; height: 64px; line-height: 64px;
text-align: center; vertical-align: middle; overflow: hidden;
}
div.numWrap {
float: left; height: 64px; line-height: 64px;
width: 80%; vertical-align: middle;
overflow: hidden; position: relative; /* relatively positioned */
}
div.strip {
position: absolute; left: 0px; /* absolutely positioned */
width: auto; white-space: nowrap;
transition: left 1s; /* instead of jquery animate */
}
With this structure, we can now use left to control the scrolling.
For partially obscured numbers, try to gently focus-in (nudge into view) a number which is partially obscured. This can be done by checking the position relative to parent and adding the width/margin to it and also accounting for width of the left arrow (it might peep thru).
Javascript:
function focus(elem) {
var stripPos = $strip.position(),
numPos = $(elem).offset(),
elemWidth = $(elem).width() + margin,
numRight = numPos.left + elemWidth;
// if it is towards right side, nudge it back inside
if (numRight > wrapWidth) {
$strip.css({"left": stripPos.left - elemWidth});
}
// if it is towards left side, nudge it back inside
if (numPos.left < (margin + $leftArrow.width())) {
$strip.css({"left": stripPos.left + elemWidth});
}
}
Once the user has scrolled the list too far and then tries to click on previous / next buttons to select a question, then we need to move the entire container upto the selected number. We can easily do this by multiplying the question number with element width and then changing the left in positive (if towards right) or in negative (if towards left).
Javascript:
// if left of element is more than the width of parent
if (numPos.left > wrapWidth) {
$strip.css({"left": -($sel.text()) * $sel.width() });
}
// if right of element is less than 0 i.e. starting position
if (numRight < 0) {
$strip.css({"left": +($sel.text()) * $sel.width() });
}
Here is a fiddle to play with: http://jsfiddle.net/abhitalks/aw166qhx/
You will need to further adapt it to your use-case, but you get the idea.

Making range output responsive

Ok, so the problem I'm having is making the output for the range responsive.
It's currently working fine when you run the code because the value is being position in pixels, but when you resize the viewport is doesn't align with the slider thumb correctly. I thought maybe aligning the output using percentages instead of pixels might fix the problem but I'm not sure how to implement it correctly.
I have tried messing around with it but no luck, does anyone know how I can achieve this?
HTML:
<form>
<div class="range-control" data-thumbwidth="20">
<input id="inputRange" type="range" min="0" max="100" step="1" value="0">
<div><output name="rangeVal">0</output></div>
</div>
</form>
CSS:
*,
*:before,
*:after {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
html {
font-family: Arial, sans-serif;
}
form {
padding-top: 100px;
margin: 0 auto;
}
.range-control {
position: relative;
}
input[type=range] {
display: block;
width: 100%;
margin: 0;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
outline: none;
}
input[type=range]::-webkit-slider-runnable-track {
position: relative;
height: 12px;
border: 1px solid #b2b2b2;
border-radius: 5px;
background-color: #e2e2e2;
box-shadow: inset 0 1px 2px 0 rgba(0,0,0,0.1);
}
input[type=range]::-webkit-slider-thumb {
position: relative;
top: -5px;
width: 20px;
height: 20px;
border: 1px solid #999;
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
background-color: #fff;
box-shadow: inset 0 -1px 2px 0 rgba(0,0,0,0.25);
border-radius: 100%;
cursor: pointer;
}
output {
position: absolute;
top: 20px;
width: 50px;
height: 24px;
border: 1px solid #e2e2e2;
margin-left: -15px;
background-color: #fff;
border-radius: 3px;
color: #777;
font-size: .8em;
line-height: 24px;
text-align: center;
}
input[type=range]:active + output {
display: block;
}
JQUERY:
$('.range-control').each(function(){
var container = $(this),
input = container.find('input'),
output = container.find('output'),
rangeWidth = input.width(),
thumbWidth = container.attr('data-thumbwidth'),
startValue = input.val(),
startOffset = ((rangeWidth - thumbWidth) / 100) * startValue + '%';
output
.css({
'left' : startOffset
});
$(input).on('input', function(){
var value = this.value,
offset = ((rangeWidth - thumbWidth) / 100) * value;
output
.val(value)
.css({
'left' : offset
});
});
});
JSFiddle
Any help would be appreciated greatly!!!
** EDIT ** Please Read
So below Mohamed-Yousef answered the question in the way he would do it which does work, so I have up voted it, however he has duplicated the variables twice in the code (see his answer for details about this). I think there is a more efficient way of doing this (using less code), so if anyone has a better way of doing this please share.
simply you need to update your variable on window resize to change its values .. and make your input event inside each function print it more than one time and that make it not available to read a new variable after resize .. so I take it out and run each event individual
AFTER EDIT
$(document).ready(function(){
var container,input,output,rangeWidth,thumbWidth,startValue,startOffset;
// make a function to update variable
var update_variable = function(){
$('.range-control').each(function(){
container = $(this);
input = container.find('input');
output = container.find('output');
rangeWidth = input.width();
thumbWidth = container.attr('data-thumbwidth');
startValue = input.val();
startOffset = ((rangeWidth - thumbWidth) / 100) * startValue;
output
.css({
'left' : startOffset
});
});
}
// update variable after document ready
update_variable();
// input input event
$('.range-control > input').on('input', function(){
var value = this.value,
offset = ((rangeWidth - thumbWidth) / 100) * value;
$(this).closest('.range-control').find('output')
.val(value +'%')
.css({
'left' : offset
});
});
// update variable in window resize
$(window).on('resize',update_variable);
});
DEMO HERE
Important Note: this code will work perfect if you have inputs with the same width .. if its not the same width you need to use an array of elements to get each element width

Categories