Javascript: Check collision between two divs - javascript

Is there any way to check, if DIV with name for example "character" is overlaping DIV with name "ground" ?
I want to do this with clean Javascript, I know that jQuery is better, but that's what I don't want.
I saw this post: check collision between certain divs? , but it does not return anything.
Thanks for help.

First, I'd suggest you check out the HTML5 canvas element, as by the sounds of it, you want to make a game, and the canvas is great for that ;)
But, to answer your question, you could create or get div elements with document.createElement() or getElementById() respectively, and get their style properties either by getting their JS-set values (element.style) or use getComputedStyle if you'd prefer to set initial values in CSS.
Make sure that, however you get these CSS properties, they'll need to be parsed into something that JS can digest. For integer-based positions, parseInt() usually does the trick.
Next, you do the math. In this case, you'd want to see if the character div's top, plus its height, is greater than the top position of the ground. If it is, it has collided.
To set the style back to the div, you can just set the style property.
Here's an example (copied from this fiddle):
var character = document.getElementById("character");
var ground = document.getElementById("ground");
//We could use getComputedStyle to get the style props,
//but I'm lazy
character.style.top = "10px";
character.style.height = "40px";
ground.style.top = "250px";
//For every 33ms (about 30fps)
setInterval(function(){
//Get the height and position of the player
var charTop = parseInt(character.style.top),
charHeight = parseInt(character.style.height);
//and the top of the ground
var groundTop = parseInt(ground.style.top);
//linear gravity? Why now?
charTop += 5;
//If the character's bottom is hitting the ground,
//Stop moving
if(charTop + charHeight > groundTop) {
charTop = groundTop - charHeight;
}
//Set the character's final position
character.style.top = charTop + "px";
},33);
#character {
position: absolute;
width: 40px;
height: 40px;
left: 50px;
background-color: #F00;
}
#ground {
position: absolute;
width: 300px;
height: 60px;
left: 0px;
background-color: #A66;
}
<div id="character"></div>
<div id="ground"></div>
One more thing: While there are convoluted ways to get element positions when elements use different positioning properties (ex: the player uses top/left coordinates, where the ground uses bottom), it's a lot harder to manage.

The only jQuery that was being used in that linked answer was to get with width,height, and position of the divs, which are somewhat trivial to retrieve using pure JS:
CSS / JavaScript - How do you get the rendered height of an element?
jquery position() in plain javascript
How do I retrieve an HTML element's actual width and height?
It's not returning anything because the .top .left and height variables in the return statement were relying on jQuery functions that retrieve the information mentioned above.

Related

Parallax - Element Glitching

So I've been trying to wrap my head around this neat effect called Parallax. Where basically the background scrolls slower than the foreground elements.
I've found this new "trick" which is working. Changing the top property to create the parallax effect, as the scrolling goes.
The issue...
So, for performance purposes and lifting the stress from the CPU when the element is not inside the user's viewport, I've created an if statement, which checks if the top position is more than 300px. If it is, it overwrites everything and sets the top property back to 0, so it won't keep increasing it for no reason.
Now, just scroll for a bit. See how, as the red div comes over the white one, the white one stutters? Looking in the DOM inspector, I see that the if statement is freaking out, setting the top property to 0px even if it's not more than 300px. Halp.
While we're at it, I'd love to see more suggestions regarding parallax effects. I've seen a few answers already regarding this effect, but they seem... overly complicated for me. I know there are better ways to do this, I know there are.
And also, It would be greatly appreciated if there were no jQuery answers. Thanks.
var txtfirst = document.getElementById("txtfirst");
window.onscroll = function(){
var ypos = window.pageYOffset;
txtfirst.style.top = ypos * 0.4 + "px";
if(txtfirst.style.top > '300px'){
txtfirst.style.top = '0px';
}
}
html, body {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
}
.text-first {
display: flex;
text-align: center;
justify-content: center;
align-items: center;
font-size: 32px;
font-family: Arial;
color: gray;
width: 100%;
height: 500px;
position: relative;
}
.foreground-red {
width: 100%;
height: 600px;
background-color: red;
display: flex;
justify-content: center;
align-items: center;
font-family: Arial;
color: gray;
font-size: 32px;
}
.spacer { /*for scrolling purposes*/
width: 100%;
height: 1000px;
}
<div class="text-first" id="txtfirst">THIS IS SOME TEXT</div>
<div class="foreground-red">THIS SHOULD GO ABOVE</div>
<div class="spacer"></div>
Your application most likely (you only provided selected snippets, I assume) does not work at least because you are comparing text when you want to be comparing numbers, in your attempt at optimization:
txtfirst.style.top > '300px'
The above will not behave like what you'd expect it to. Every property of the Element::style property (e.g. txtfirst.style in your case) is a text string, not a number. A test like "50px" < "300px" does not compare whether 50 is less than 300, it compares the text values lexicographically.
If you actually want to compare the amount of pixels, you can use parseInt function to convert a value like 50px to a number, 50. Your test will then look as follows:
parseInt(txtfirst.style.top) < 300
Now, what follows is a number of problems with your approach to solving this and suggested solutions, since you are interested in suggestions.
Using inline styles is problematic in general (subjective)
Inline styles have the highest precedence in CSS, which can be problematic in cases where the user has their own style sheets, as properties set in those will be ignored in favor of properties set inline.
Reading properties of inline style back assuming that would be the actual used value, is just plain wrong. Inline style object tracks assigned values, not computed or used values. The Window::getComputedStyle(element) function, on the other hand, retrieves computed style for the element.
Solution? Reading properties using getComputedStyle and writing them directly to a preferred (or empty, if so desired) stylesheet (document.styleSheets, reflecting all link rel=stylesheet and style elements):
function rule(selector) {
var sheet = document.styleSheets[0];
return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
window.onscroll = function() {
var ypos = window.pageYOffset;
var style = rule(".text-first").style;
style.top = ypos * 0.4 + "px";
if(parseInt(getComputedStyle(txtfirst).top) > 300) {
style.top = "0px";
}
}
The rule function above returns the CSS rule (one containing the set CSS properties) with matching selector (e.g. .text-first or html, body) from the first found stylesheet (you only have one). The style property of a a rule refers to an object which contains all CSS properties set in the rule. It behaves the same as the inline style object. Observe that you aren't using inline styles anywhere above, you write to the stylesheet object (as initialized by the <style>...</style> fragment of your document) and read back computed values.
Fixing problems with using scroll event for animation
First of all, did you know that older versions of iOS did not fire the scroll event as you scrolled? That would stop your parallax effect dead in its tracks, as a single scroll event would be fired after the user stops scrolling. This has to do with the way browsers do page scrolling -- to achieve smooth page scrolling animation using constrained mobile CPU resource, running JavaScript code courtesy of a scroll event handler 60 times per second was just deemed too generous an offer, and Apple instead went for the controversial solution, occupied with good UX as they are.
Anyway, what to do then if not use scroll event? You could use the good old setInterval:
function rule(selector) {
var sheet = document.styleSheets[0];
return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
var old_window_pageYOffset = window.pageYOffset;
setTimeout(function() {
var ypos = window.pageYOffset;
if(ypos != old_window_pageYOffset) return;
old_window_pageYOffset = ypos;
var style = rule(".text-first").style;
style.top = ypos * 0.4 + "px";
if(parseInt(getComputedStyle(txtfirst).top) > 300) {
style.top = "0px";
}
}, 1000 / 60);
What the above does is makes sure a function is called 60 times per second for the entire lifetime of your page, but checks on every invocation if the scroll position of the window has changed since last invocation, invoking the old code only if it has, and doing nothing otherwise. This obviously does not use the scroll event at all. All this said, newer iOS releases have since reverted the behavior and the scroll event is fired with every change of the scrolling position. Meaning you may simply want to use that as baseline and depend on the event instead of setInterval. A free benefit of the latter is that you control the rate at which your parallax effect runs.
I can also suggest using requestAnimationFrame, which is more "intelligent" than setInterval in that the user agent will not invoke your code if it deems animation unnecessary, for example if the entire page tab isn't visible or interactive with the user at the moment. Rest assured your animation will run "when needed":
function rule(selector) {
var sheet = document.styleSheets[0];
return Array.prototype.find.call(sheet.cssRules, rule => rule.selectorText == selector);
}
var txtfirst = document.getElementById("txtfirst");
var old_window_pageYOffset = window.pageYOffset;
requestAnimationFrame(function() {
var ypos = window.pageYOffset;
if(ypos != old_window_pageYOffset) return;
old_window_pageYOffset = ypos;
var style = rule(".text-first").style;
style.top = ypos * 0.4 + "px";
if(parseInt(getComputedStyle(txtfirst).top) > 300) {
style.top = "0px";
}
});
The code above is an okay attempt at the parallax effect, save for minor nitpicks which have little to do with parallax effect alone:
I don't use the on*name* family of functions when we have addEventListener. The former is one property for each handler, and there are no guarantees your script is the sole consumer of these properties -- they may already be set by a browser extension. We can argue whether the Web page author has exclusive ownership and access to all properties they can get their hands on, but at least I have explained my rationale. There is no significant drawback known to me for using addEventListener("scroll", function() { ... }) instead.
You don't need to use both a class name and an ID for an element to refer to it. document.querySelector(".text-field") will return the first available element that has "text-field" among its list of class names.
I've saved the best for last -- Pure CSS Parallax Websites goes through achieving (although not without some small hacks for browsers with bugs) desired effect without any JavaScript at all, relying on the CSS perspective property and some others. It also mentions some of the same things I've warned about above, things that I have attempted to circumvent and explain.
If you don't want to read (and understand) documentation, I suggest you resort to using a convenient abstraction -- a plugin, a framework, a library or something to that end that will save you from having to grok the intricacies of this. Modern CSS and compliant browser model are complex enough for these solutions to exist and thrive.

position a border around focused element

I want that the focused element (mostly using the tab key navigation) appears with a surrounding rectangle the size of the element. I created the appropriated div in html with the appropriate css for border color and a display none at the beginning of the navigation. I am trying to change the display with jquery (the size and the position) but something is going wrong.
jQuery('*').focus(function () {
var position = jQuery(this).offset();
var width = jQuery(this).width();
var height = jQuery(this).height();
console.log(jQuery(this).width(), jQuery(this).height(), position);
jQuery('#focuser').fadeOut(0); //to have the div disappear if it is on other element
jQuery('#focuser').css({width: '2px', height: '2px'}); //to get an enlarging effect
jQuery('#focuser').offset({top: position.top, left: position.left});
jQuery('#focuser').fadeIn(100, function () {
jQuery('#focuser').animate({width: width, height: height}, 200);
});
console.log(jQuery('#focuser').width(), jQuery('#focuser').height(), jQuery('#focuser').offset());
});
The position retrieve of jQuery(this) is correct but when I set it to offset, it seems to add the value instead of replacing it. Am I missing something ? Is that the correct way to manage it (a full css solution with pseudo element :focus does not work as the border is added to the element size and destroys the page display, the border needs to have an animation ) ?
#focuser {
border: 2px $second-font-color solid;
display: none;
position: absolute;
}
Thanks for any help
I think I found the solution but I do not understand why it is like that. I put the fadeout before the variables setting and it looks to work.
jQuery('*').focus(function () {
jQuery('#focuser').fadeOut(0);
var position = jQuery(this).offset();
var width = jQuery(this).width();
var height = jQuery(this).height();
jQuery('#focuser').fadeIn(0);
jQuery('#focuser').css({width: 0, height: 0});
jQuery('#focuser').offset({top: position.top, left: position.left});
jQuery('#focuser').animate({width: width, height: height}, 200);
});
You can use a basic css :focus selector without using JQuery. See W3Schools for more info.
You'll need to give a border of the same size to the element before it's focused, you can give it the same color as the background. Otherwise the border will take extra space and destroy your layout as explained here. You can also set the margin to 2px and remove it when the item is focused, that way it still enlarges.
Check this Fiddle to see what I mean: jsfiddle.net/GillesCoeman/30vhjtdh/3. It also uses CSS transition to add the effect.
If this is not exactly what you are looking for you could also use a combination of this and jQuery.

CSS3/HTML5/JS - How can I create an animated texture background with slide effect?

You may have seen that in some games there's moving background that goes on diagonal. Take a look at this example of Megaman x Storm Eagle Stage https://youtu.be/Wfm3ZvcxOKQ?t=1m33s, the clouds are moving on from up to bottom, right to left (Diagonal). I need to achieve the same effect using CSS3 or any kind of javascript, with a tile texture.
Where can I find a tutorial teaching how to make that? I found some, but they are for creating dynamic objects.
Hi this could be solved using basic javascript or any javascript library. I will give a small working example or you can just jump to this js fiddle https://jsfiddle.net/lesshardtofind/a2Lx51xy/
In your html you just need one element.
<div class='background'></div>
The class is for us to select it. First with some css lets make it so we have a picture and can see it.
.background{
background-image: url('yourImageUrl.com/image.png');
height: 500px;
width: 500px;
}
So background image applies your texture, height, and width will apply values so that you can see the div otherwise it would be 0 with no actual markup inside.
Now for the fun part. JavaScript can grab this div element and apply a style. Normally I would do this in jQuery, but since you just specified javaScript I'll keep external libraries out of it.
var x = 0;
var y = 0;
var xSpeed = 5;
var ySpeed = 5;
var interval = setInterval(function(){
var xVal = x + 'px';
var yVal = y + 'px';
var bg = document.getElementsByClassName('background')[0].style.backgroundPosition = xVal + " " + yVal;
x-=xSpeed;
y+=ySpeed;
}, 100);
To explain this I set x and y which are my background coordinates to 0 as a start. It is good to set default values otherwise you will invite undefined behavior. xSpeed, and ySpeed are just variables you can change the value of speed up or slow down the animation. interval is the variable that stores the interval set by the setInterval function. setInterval accepts a function and a time value as arguments. The function will be called at each interval and the time is the amount of time to wait until calling the function again.
document.getElementsByClassname('background') returns an array which I access with the [0] index. Then style allows you to apply a value to your backgroundPosition. Notice that I applied the 'px' to the end of each number so that they would be a string such as "3px 5px" this is how they are expected.
Then speed is applied using -= and +=. Since the dom starts at 0, 0 in the left corner of your browser then adding to Y will move down and subtracting from X will move left.
Lastly this works because background is set by default to repeat so the image will continue to just driftdownward and to the left. If you supply a tiled image you won't see the seams in between one repeat and the next.
Edit: Add fullscreen
You can add the fullscreen to your child object by making sure that its parents also have a set width and height. So changing your css to.
body, html{
height: 100%;
width: 100%;
}
.background{
background-image: url('http://bgfons.com/upload/sky_texture1998.jpg');
height: 100%;
width: 100%;
border: 1px solid red;
}
On % heights you need a parent node to inherit height from.

How do I get the height of a textarea

I need to get the height of a textarea. Seemingly so simple but it's driving me mad.
I have been researching for ages on stackoverflow with no luck: textarea-value-height and jquery-js-get-the-scrollbar-height-of-an-textarea and javascript-how-to-get-the-height-of-text-inside-of-a-textarea, among many others.
This is how it looks currently:
This is how I want it to look, open a full height:
.
Here is my html:
<textarea id="history" class="input-xxlarge" placeholder="Enter the content ..." rows="13"></textarea>
CSS:
.input-xxlarge {
display: inline-block;
height: auto;
font-size: 12px;
width: 530px;
resize: none;
overflow: auto;
}
jQuery:
var textarea = $('#history');
I've tried (inter alia):
1. textarea.height() --> always returns 0
2. textarea.ready(function() { // wait for DOM to load
textarea.height();
}
3. getting scrollheight from textarea as an HTMLTextareaElement (i.e. DOM Element) --> returns 0
4. var contentSpan = textarea.wrapInner('<span>');
var height = contentSpan.height(); --> always returns 0
Please help, I'm at my wit's end!
Ok, I've found a solution. Whether it's the best solution, I don't know, but it works and that, frankly, is all I care about, having spent almost a day on this issue.
Here it is for anyone who faces the same problem:
Select the textarea:
var textarea = $('#history');
Get the textarea's text:
var text = textarea.text();
Create a temporary div:
var div = $('<div id="temp"></div>');
Set the temp div's width to be the same as the textarea. Very important else the text will be all on one line in the new temp div!:
div.css({
"width":"530px"
});
Insert the text into the new temp div:
div.text(text);
Append it to the DOM:
$('body').append(div);
Get the height of the div:
var divHeight = $('#temp').height();
Remove the temp div from the DOM:
div.remove();
Had a similar issue, in my case I wanted to have an expand button, that would toggle between two states (expanded/collapsed). After searching also for hours I finally came up with this solution:
Use the .prop to get the content height - works with dynamically filled textareas and then on a load command set it to your textarea.
Get the inner height:
var innerHeight = $('#MyTextarea').prop('scrollHeight');
Set it to your element
$('#MyTextarea').height(innerHeight);
Complete code with my expand button(I had min-height set on my textarea):
$(document).on("click", '.expand-textarea', function () {
$(this).toggleClass('Expanded');
if($(this).hasClass('Expanded'))
$($(this).data('target')).height(1);
else
$($(this).data('target')).height($($(this).data('target')).prop('scrollHeight'));
});
Modern answer: textarea sizing is a few lines of ES6 implementable two primary ways. It does not require (or benefit from) jQuery, nor does it require duplication of the content being sized.
As this is most often required to implement the functionality of auto-sizing, the code given below implements this feature. If your modal dialog containing the text area is not artificially constrained, but can adapt to the inner content size, this can be a perfect solution. E.g. don't specify the modal body's height and remove overflow-y directives. (Then no JS will be required to adjust the modal height at all.)
See the final section for additional details if you really, truly only actually need to fetch the height, not adapt the height of the textarea itself.
Line–Based
Pro: almost trivial. Pro: exploits existing user-agent behavior which does the heavy lifting (font metric calculations) for you. Con: impossible to animate. Con: extended to support constraints as per my codepen used to explore this problem, constraints are encoded into the HTML, not part of the CSS, as data attributes.
/* Lines must not wrap using this technique. */
textarea { overflow-x: auto; white-space: nowrap; resize: none }
for ( let elem of document.getElementsByTagName('textarea') ) {
// Prevent "jagged flashes" as lines are added.
elem.addEventListener('keydown', e => if ( e.which === 13 ) e.target.rows = e.target.rows + 1)
// React to the finalization of keyboard entry.
elem.addEventListener('keyup', e => e.target.rows = (elem.value.match(/\n/g) || "").length + 1)
}
Scrollable Region–Based
Pro: still almost trivial. Pro: animatable in CSS (i.e. using transition), though with some mild difficulty relating to collapsing back down. Pro: constraints defined in CSS through min-height and max-height. Con: unless carefully calculated, constraints may crop lines.
for ( let elem of document.getElementsByTagName('textarea') )
elem.addEventListener('keyup', e => {
e.target.style.height = 0 // SEE NOTE
e.target.style.height = e.target.scrollHeight + 'px'
})
A shocking percentage of the search results utilizing scrollHeight never consider the case of reducing size; for details, see below. Or they utilize events "in the wrong order" resulting in an apparent delay between entry and update, e.g. pressing enter… then any other key in order to update. Example.
Solution to Initial Question
The initial question specifically related to fetching the height of a textarea. The second approach to auto-sizing, there, demonstrates the solution to that specific question in relation to the actual content. scrollHeight contains the height of the element regardless of constraint, e.g. its inner content size.
Note: scrollHeight is technically the Math.max() of the element's outer height or the inner height, whichever is larger. Thus the initial assignment of zero height. Without this, the textarea would expand, but never collapse. Initial assignment of zero ensures you retrieve the actual inner content height. For sampling without alteration, remove the height override (assign '') or preserve (prior to) then restore after retrieval of scrolllHeight.
To calculate just the height of the element as-is, utilize getComputedStyle and parse the result:
parseInt(getComputedStyle(elem).height, 10)
But really, please consider just adjusting the CSS to permit the modal to expand naturally rather than involving JavaScript at all.
Place this BEFORE any HTML elements.
<script src="/path/to/jquery.js"></script>
<script>
$(document).ready(function(){
var textarea = $('#history');
alert(textarea.height()); //returns correct height
});
</script>
You obviously do not have to alert it. I was just using an easily visible example.
Given a textarea with an id of "history", this jQuery will return it's height:
$('#history').height()
Please see a working example at http://jsfiddle.net/jhfrench/JcGGR/
You can also retrieve the height in pixels by using $('#history').css('height'); if you're not planning on doing any calculations.
for current height in px:
height = window.getComputedStyle(document.querySelector('textarea')).getPropertyValue('height')
for current width in px:
width = window.getComputedStyle(document.querySelector('textarea')).getPropertyValue('width')
change 'textarea' to '#history' or like a css selector. or textarea, since a variable is declared to select element.

How to make jQuery to not round value returned by .width()?

I've searched around and couldn't find this. I'm trying to get the width of a div, but if it has a decimal point it rounds the number.
Example:
#container{
background: blue;
width: 543.5px;
height: 20px;
margin: 0;
padding: 0;
}
If I do $('#container').width(); it will return 543 instead of 543.5. How do I get it to not round the number and return the full 543.5 (or whatever number it is).
Use the native Element.getBoundingClientRect rather than the style of the element. It was introduced in IE4 and is supported by all browsers:
$("#container")[0].getBoundingClientRect().width
Note: For IE8 and below, see the "Browser Compatibility" notes in the MDN docs.
$("#log").html(
$("#container")[0].getBoundingClientRect().width
);
#container {
background: blue;
width: 543.5px;
height: 20px;
margin: 0;
padding: 0;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container"></div>
<p id="log"></p>
Ross Allen's answer is a good starting point but using getBoundingClientRect().width will also include the padding and the border width which ain't the case the the jquery's width function:
The returned TextRectangle object includes the padding, scrollbar, and
the border, but excludes the margin. In Internet Explorer, the
coordinates of the bounding rectangle include the top and left borders
of the client area.
If your intent is to get the width value with the precision, you'll have to remove the padding and the border like this:
var a = $("#container");
var width = a[0].getBoundingClientRect().width;
//Remove the padding width (assumming padding are px values)
width -= (parseInt(a.css("padding-left")) + parseInt(a.css("padding-right")));
//Remove the border width
width -= (a.outerWidth(false) - a.innerWidth());
Just wanted to add my experience here, though the question's old: The consensus above seems to be that jQuery's rounding is effectively just as good as an unrounded calculation -- but that doesn't seem to be the case in something I've been doing.
My element has a fluid width, generally, but content that changes dynamically via AJAX. Before switching the content, I temporarily lock the dimensions of the element so my layout doesn't bounce around during the transition. I've found that using jQuery like this:
$element.width($element.width());
is causing some funniness, like there are sub-pixel differences between the actual width and the calculated width. (Specifically, I will see a word jump from one line of text to another, indicating the the width has been changed, not just locked.) From another question -- Getting the actual, floating-point width of an element -- I found out that window.getComputedStyle(element).width will return an unrounded calculation. So I changed the above code to something like
var e = document.getElementById('element');
$('#element').width(window.getComputedStyle(e).width);
And with THAT code -- no funny bouncing! That experience seems to suggest that the unrounded value actually does matter to the browser, right? (In my case, Chrome Version 26.0.1410.65.)
You can use getComputedStyle for it:
parseFloat(window.getComputedStyle($('#container').get(0)).width)
Use the following to get an accurate width:
var htmlElement=$('class or id');
var temp=htmlElement[0].style.width;

Categories