Inside a position:relativeelement box, I have
a absolutely positioned element parent at top:0 with a couple of static p and h1..h6inside. Those may all have different margin/padding values.
a few absolutely positioned elements where element name and content matches exactly one of the elements from above list #1.
The goal is to set the y-value of the 2nd element such that it exactly overlaps with the corresponding one from the set in 1. .
I've taken this approach (using closure, but anything could be used really), starting with an Array of elements contentfor generating list #1:
goog.dom.removeChildren(parent);
for (var i=0; i<content.length; i++) {
offsets.push(parent.offsetHeight);
goog.dom.appendChild(parent, content[i]);
}
return offsets;
Then, using the values from offsets:
var matchedElm = source[i].cloneNode(true);
goog.style.setStyle(matchedElm, {"top": offsets[i] + "px"});
goog.dom.appendChild(box, matchedElm);
Now this works for various paddings and margin=0, but fails when margins are non-0 on the p and h1..6. I have also tried "scrollHeight" but nothing seems to take into account margins.
I've then tried getting offsetTop directly from the rendered elements in list #1, but this seems to yield exact same results as above procedure to fill offsets.
How can I make the overlap work? I'm looking for a native javascript or closure-library solution.
What you are looking for may be goog.style.getPageOffset and because your overlapping element is absolutely positioned with a top setting you may have to deduct the top margin of the element you want to overlap using goog.style.getMarginBox. Here is some sample code:
<!DOCTYPE html>
<html>
<head>
<title>test</title>
</head>
<body>
<div id="game" data-something="hello"></div>
<script type="text/javascript" src="js/goog/base.js"></script>
<script type="text/javascript">
goog.require("goog.dom");
goog.require("goog.style");
</script>
</body>
<div id="contentToOverLap" style="position:absolute;top:30px;left:30px">
<h1 style="margin:10px;padding:2px">h1 10px mar and 2px pad</h1>
</div>
<div id="overlappingContent" style="display:inline">
<h1 style="color:red;margin:15px;padding:2px;position:absolute">
h1 15px mar and 2px pa
</h1>
</div>
<script type="text/javascript">
(function () {
var pH = goog.dom.$("contentToOverLap").children[0];
var cH = goog.dom.$("overlappingContent").children[0];
var box = goog.style.getPageOffset(pH);
console.log("box is:", box);
var mar = goog.style.getMarginBox(cH);
console.log("mar is:", mar);
var t = box.y - mar.top;
var l = box.x - mar.left;
goog.style.setStyle(cH, { "top": t + "px", "left": l + "px" });
})();
</script>
</html>
Related
I have an image of a black cat
I want to progressively increase its width and also make the image moving from left to right increasing its own left margin.
I can't understand why the width is increased and the margin-left shows NaN value.
Down here the Html :-
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Cat Walk</title>
</head>
<body>
<img id="cat-gif" style="position:absolute;" src="http://www.anniemation.com/clip_art/images/cat-walk.gif">
<script src="main.js"></script>
</body>
Down here the function to make move the cat:-
var catWalk = function() {
var cat = document.getElementById("cat-gif");
cat.setAttribute('width', cat.width+10); //THIS WORKS
cat.setAttribute('marginLeft', cat.marginLeft+10); //THIS DOES NOT WORK!
}
//Call to the function
window.setInterval(catWalk, 500);
try using the following:
cat.style.marginLeft = (parseInt((cat.style.marginLeft) || parseInt(window.getComputedStyle(cat).marginLeft))) + 10 + 'px';
instead of cat.setAttribute('marginLeft', cat.marginLeft+10); there is no attribute marginLeft by default on your Element. So you gotta go by getting the computed styles and add it to your style attribute then you can access it.
Margin left is not a attribute hence it does not work in your case.
its a style property hence you need to do it differently
replace
cat.setAttribute('marginLeft', cat.marginLeft+10);
with
var p = cat.style.marginLeft; // return value in px; i.e 50px
p = p.substr(0,p.length-2); // remove px ie : 50px becomes 50
cat.style.marginLeft = (+p)+ 10 +'px' // convert p to number and add 10
I'm not sure how to explain what I exactly want (which makes it really hard to google for as well), but I would like to create a table with each cell a specific width and height (let's say 40x40 pixels).
This table will be way larger than the viewport, so it would need some scrolling, but I don't want scrollbars (this part ain't a problem), but a way to drag the table around "behind the viewport".
To make it a bit more complex, I need to be able to center a specific cell and know which cell is centered too (although if I know for example that the table is on left: -520px; I can calculate what the situation is).
Long story short, I'm looking for something that looks and works like maps.google.com, but then with a table and cells instead of a canvas or divs.
What you're trying to achieve is relatively simple. You have a container div element with position: relative; overflow: hidden applied via CSS and the content set to position: absolute. For example:
<div class="wrapper">
<div class="content-grid">
... your grid HTML ...
</div>
</div>
You then need to set up some mouse/touch tracking javascript, you can find plenty of examples around Stack Overflow or Google, which monitors the position of the mouse at mousedown or touchstart events and then tests this repeatedly on an interval to see where the pointer is and update the content-grid top and left position, until mouseup or touchend.
You can make this animation smooth using CSS transition on left and top.
The tricky bit is calculating the position for the centre cell. For this I would recommend calculating a central zone, the size of your grid cells (i.e. 40x40) in the middle of your container element. Then checking if any grid cell is currently more than 1/4 inside that zone, and treating it as the "central" element.
Here is a basic example for position a cell within a grid within a wrapper: https://jsfiddle.net/tawt430e/1/
Hope that helps!
I was a bit disappointed to see the down votes of my question at first. I can imagine that stackoverflow has a lot of issues with new people just trying to get their code written here, but that's not what I asked.
Anyway, with the "you share the problem, you share the solution", mindset, I fixed the code with help of tw_hoff and it all works now. It even saves the coordinates in the local storage so this example HTML keeps you in the same position if you refresh the page. I added the two example images I used as well (store the left one as farm.png, the right one as tile.png, same directory as the html page).
The actual code:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Game map demo</title>
<style>
body { margin: 0; padding: 0; }
</style>
<script src="https://code.jquery.com/jquery-1.10.2.js"></script>
</head>
<body>
<div id="map" style="position: absolute; overflow: hidden; width: 100%; height: 100%; background-color: darkgreen;">
<div id="content" style="white-space: nowrap;">
</div>
</div>
<script>
for(i = 0; i < 20; i++) {
for(j = 0; j < 20; j++) {
var tile;
if((i == 4 || i == 5) && (j == 2 || j == 3)) {
tile = 'farm';
} else {
tile = 'tile';
}
$("#content").append('<div style="background: url(\'' + tile + '.png\'); width: 128px; height: 128px; position: absolute; margin-left: ' + (i * 128) + 'px; margin-top: ' + (j * 128) + 'px;"></div>');
}
}
$("body").css("-webkit-user-select","none");
$("body").css("-moz-user-select","none");
$("body").css("-ms-user-select","none");
$("body").css("-o-user-select","none");
$("body").css("user-select","none");
var down = false;
var current_left = 0;
var current_top = 0;
if(localStorage.getItem("current_left") && localStorage.getItem("current_top")) {
current_left = Number(localStorage.getItem("current_left"));
current_top = Number(localStorage.getItem("current_top"));
console.log(current_left);
$("#content").css('marginLeft', (current_left) + 'px');
$("#content").css('marginTop', (current_top) + 'px');
}
$(document).mousedown(function() {
down = true;
}).mouseup(function() {
down = false;
});
var cache_pageX;
var cache_pageY;
$( "#map" ).mousemove(function( event ) {
if(down == true) {
current_left += (-1 * (cache_pageX - event.pageX));
if(current_left > 0)
current_left = 0;
if(current_left < (-2560 + $("#map").width()))
current_left = (-2560 + $("#map").width());
current_top += (-1 * (cache_pageY - event.pageY));
if(current_top > 0)
current_top = 0;
if(current_top < (-2560 + $("#map").height()))
current_top = (-2560 + $("#map").height());
localStorage.setItem("current_left", current_left);
localStorage.setItem("current_top", current_top);
$("#content").css('marginLeft', (current_left) + 'px');
$("#content").css('marginTop', (current_top) + 'px');
}
cache_pageX = event.pageX;
cache_pageY = event.pageY;
});
</script>
</body>
</html>
It still needs some work, and of course a lot of work to be actually used in a game, but this part works and I hope if someone else ever might have the same issue and searches for it on stackoverflow my solutions gives them a push in the right direction :).
Thanks for those that helped!
I've been trying to create a small algorithm that adds a class attr to a specific element if the mouse reaches the X plain half of the browser or more.
I am also going to provide you with a screenshot where I need to use this kind of application.
http://tinypic.com/view.php?pic=2ur101d&s=8#.VVsOR0ai2Uk
As you can see in the image above, if the poster is too far on the right the div that appears on hover stretches out of the browser.
Thats why i need to add that class to move it a bit when the mouse reaches that distance on X.
Here's what i've tried so far but the if condition that I applied does not work.
<!DOCTYPE html>
<html>
<head>
<script>
function readMouseMove(e) {
var result_x = document.getElementById('x_result');
var result_y = document.getElementById('y_result');
result_x.innerHTML = e.clientX;
result_y.innerHTML = e.clientY;
var distance = window.innerWith;
var math = distance / 2;
if (e.clientX >= math) {
result_x.setAttribute('class', 'special');
}
}
document.onmousemove = readMouseMove;
</script>
<style>
.special {
color: red;
}
</style>
</head>
<body>
<h1>Document that reads mouse movement</h1>
<h2 id="x_result">0</h2>
<h2 id="y_result">0</h2>
</body>
</html>
I just refactored your condition, there was a spelling mistake:
if(e.clientX >= window.innerWidth / 2) {
result_x.setAttribute('class','special');
}
I have a debugger log that I've written in JavaScript for a project I'm working on. The log is basically an <aside> tag in HTML5 that only shows when needed. I wanted to play around with the idea of being able to move the log around the screen, as it may overlap certain things (which is fine for my project). However, I can't seem to figure out how to use HTML5 to properly drag and drop the tag so that it can be placed anywhere on the screen (well, or within a <div> element).
After reading on HTML5's drag and drop support, I have a basic understanding of how it works, but I'm not sure where to start when it comes to allowing the div to be placed anywhere (it's z-index is a high value, so as I said, overlapping is fine).
Any suggestions?
Oh, and I'd like to try and avoid using external libraries for this project, wherever possible. I'm trying to do this in pure JavaScript/HTML5.
Drag and drop doesn't move elements around, if you want the element to move when you drop it then you have to set the new position of the element in the drop event. I've done an example which works in Firefox and Chrome, here are the key points:
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
event.dataTransfer.setData("text/plain",
(parseInt(style.getPropertyValue("left"),10) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top"),10) - event.clientY));
}
The dragstart event works out the offset of the mouse pointer from the left and top of the element and passes it in the dataTransfer. I'm not worrying about passing the ID because there's only one draggable element on the page - no links or images - if you have any of that stuff on your page then you'll have to do a little more work here.
function drop(event) {
var offset = event.dataTransfer.getData("text/plain").split(',');
var dm = document.getElementById('dragme');
dm.style.left = (event.clientX + parseInt(offset[0],10)) + 'px';
dm.style.top = (event.clientY + parseInt(offset[1],10)) + 'px';
event.preventDefault();
return false;
}
The drop event unpacks the offsets and uses them to position the element relative to the mouse pointer.
The dragover event just needs to preventDefault when anything is dragged over. Again, if there is anything else draggable on the page you might need to do something more complex here:
function drag_over(event) {
event.preventDefault();
return false;
}
So bind it to the document.body along with the drop event to capture everything:
var dm = document.getElementById('dragme');
dm.addEventListener('dragstart',drag_start,false);
document.body.addEventListener('dragover',drag_over,false);
document.body.addEventListener('drop',drop,false);
If you want this to work in IE you'll need to convert the aside to an a element, and, of course, all the event binding code will be different. The drag and drop API doesn't work in Opera, or on any mobile browsers as far as I'm aware. Also, I know you said you don't want to use jQuery, but cross browser event binding and manipulating element positions are the sort of things that jQuery makes much easier.
Thanks for your answer. It works great in Chrome and Firefox. I tweaked it to work in IE.Below is the code.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="generator" content="CoffeeCup HTML Editor (www.coffeecup.com)">
<meta name="dcterms.created" content="Fri, 27 Jun 2014 21:02:23 GMT">
<meta name="description" content="">
<meta name="keywords" content="">
<title></title>
<!--[if IE]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<style>
li
{
position: absolute;
left: 0;
top: 0; /* set these so Chrome doesn't return 'auto' from getComputedStyle */
width: 200px;
background: rgba(255,255,255,0.66);
border: 2px solid rgba(0,0,0,0.5);
border-radius: 4px; padding: 8px;
}
</style>
<script>
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
var str = (parseInt(style.getPropertyValue("left")) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top")) - event.clientY) + ',' + event.target.id;
event.dataTransfer.setData("Text", str);
}
function drop(event) {
var offset = event.dataTransfer.getData("Text").split(',');
var dm = document.getElementById(offset[2]);
dm.style.left = (event.clientX + parseInt(offset[0], 10)) + 'px';
dm.style.top = (event.clientY + parseInt(offset[1], 10)) + 'px';
event.preventDefault();
return false;
}
function drag_over(event) {
event.preventDefault();
return false;
}
</script>
</head>
<body ondragover="drag_over(event)" ondrop="drop(event)">
<ul>
<li id="txt1" draggable="true" ondragstart="drag_start(event)"> Drag this text </li><br>
<li id="txt2" draggable="true" ondragstart="drag_start(event)"> Drag me</li>
</ul>
<p>I never am really satisfied that I understand anything; because, understand it well as I may, my comprehension can only be an infinitesimal fraction of all I want to understand about the many connections and relations which occur to me, how the matter in question was first thought of or arrived at, etc., etc.</p>
<p>In almost every computation a great variety of arrangements for the succession of the processes is possible, and various considerations must influence the selections amongst them for the purposes of a calculating engine. One essential object is to choose that arrangement which shall tend to reduce to a minimum the time necessary for completing the calculation.</p>
<p>Many persons who are not conversant with mathematical studies imagine that because the business of [Babbage’s Analytical Engine] is to give its results in numerical notation, the nature of its processes must consequently be arithmetical and numerical, rather than algebraical and analytical. This is an error. The engine can arrange and combine its numerical quantities exactly as if they were letters or any other general symbols; and in fact it might bring out its results in algebraical notation, were provisions made accordingly.</p>
<p>The Analytical Engine has no pretensions whatever to originate anything. It can do whatever we know how to order it to perform. It can follow analysis, but it has no power of anticipating any analytical revelations or truths. Its province is to assist us in making available what we are already acquainted with.</p>
</body>
</html>
I adjusted a bit robertc's great answer for cases of multiple items. Here the secondclass name is just for another position.
<aside draggable="true" class="dragme" data-item="0">One</aside>
<aside draggable="true" class="dragme second" data-item="1">Two</aside>
Add the data-item attribute to the setDatafunction.
function drag_start(event) {
var style = window.getComputedStyle(event.target, null);
event.dataTransfer.setData("text/plain", (parseInt(style.getPropertyValue("left"), 10) - event.clientX) + ',' + (parseInt(style.getPropertyValue("top"), 10) - event.clientY) + ',' + event.target.getAttribute('data-item'));
}
Target the element that is dragged.
function drop(event) {
var offset = event.dataTransfer.getData("text/plain").split(',');
var dm = document.getElementsByClassName('dragme');
dm[parseInt(offset[2])].style.left = (event.clientX + parseInt(offset[0], 10)) + 'px';
dm[parseInt(offset[2])].style.top = (event.clientY + parseInt(offset[1], 10)) + 'px';
event.preventDefault();
return false;
}
And loop through all elements with a class dragme.
var dm = document.getElementsByClassName('dragme');
for (var i = 0; i < dm.length; i++) {
dm[i].addEventListener('dragstart', drag_start, false);
document.body.addEventListener('dragover', drag_over, false);
document.body.addEventListener('drop', drop, false);
}
pen
In a small iframe-application at the Russian social network Ok.ru I use the following call to resize the iframe:
<div id="fb-root"></div>
<script src="http://api.odnoklassniki.ru/js/fapi.js"
type="text/javascript"></script>
<script type="text/javascript">
FAPI.init("http://api.odnoklassniki.ru/", "XXX_YYY",
function() {
alert("clientHeight " + document.getElementById("fb-root").clientHeight);
alert("offsetHeight " + document.getElementById("fb-root").offsetHeight);
FAPI.UI.setWindowSize(720, 1200);
}, function(error){
alert("API initialization failed");
});
</script>
</body>
</html>
i.e. the iframe-height is currently hardcoded to 1200.
It works okay, but I'd like to use the height/position of #fb-root element instead.
Does anybody please have an idea, which JavaScript or CSS function could be used here - if I don't want to include jQuery just for one $(#fb-root).offset() call?
I've also looked at their library http://api.odnoklassniki.ru//js/fapi.js but it doesn't include such function.
UPDATE
I've added two alert-calls to my source code above, but they only print
clientHeight 0
offsetHeight 0
UPDATE
The following code seem to work well for my iframe-app now in Google Chrome and Mozilla Firefox, regardless of how many s do I add for testing it. But with Internet Explorer it fails to resize the window and alert shows top=1107 instead of top=1157 in Google Chrome, thus the html table at the bottom is cut off:
... here my flash game + html table with players ...
<br>
<br>
<br>
<br>
<br>
<br>
<div id="fb-root"></div>
<script src="http://api.odnoklassniki.ru/js/fapi.js" type="text/javascript"></script>
<script type="text/javascript">
FAPI.init("http://api.odnoklassniki.ru/", "XXX_YYY",
function() {
var top = findTop(document.getElementById("fb-root"));
FAPI.UI.setWindowSize(720, Math.max(top, 1200));
}, function(error){
alert("API initialization failed");
});
function findTop(obj) {
if(!obj) return 0;
return obj.offsetTop + findTop(obj.offsetParent);
}
</script>
</body>
</html>
Looking at the code in jquery, the offset is calculated like this:
function getOffset(element)
{
if (!element.getClientRects().length)
{
return { top: 0, left: 0 };
}
let rect = element.getBoundingClientRect();
let win = element.ownerDocument.defaultView;
return (
{
top: rect.top + win.pageYOffset,
left: rect.left + win.pageXOffset
});
}
Quirksmode has a JavaScript tutorial/function that shows how to find the coordinates of an element here
Once you have its coordinates, you can use the offsetHeight property of the iframe to read its height.
If document.getElementById("fb-root").clientHeight returns 0, it certainly means that your "fb-root" div is either not displayed or still empty.
I have had a look at fapi.js, and it seems that calling the FAPI.init function creates a span which id is "FAPI_Flash_wrap", appends it to the body of the document, then embeds a flash object with id "FAPI_Flash" in this span.
So I would try retrieving an element with id "FAPI_Flash_wrap" or "FAPI_Flash" instead of "fb-root":<script type="text/javascript">
FAPI.init("http://api.odnoklassniki.ru/", "XXX_YYY",
function() {
alert("Height = " + document.getElementById("FAPI_Flash_wrap").offsetHeight);
alert("Width = " + document.getElementById("FAPI_Flash_wrap").offsetWidth);
FAPI.UI.setWindowSize(720, 1200);
}, function(error){alert("API initialization failed");}
);
</script>
I hope this works!
If it's the case, then just use
<script type="text/javascript">
FAPI.init("http://api.odnoklassniki.ru/", "XXX_YYY",
function() {
var height = document.getElementById("FAPI_Flash_wrap").offsetHeight;
var width = document.getElementById("FAPI_Flash_wrap").offsetWidth;
FAPI.UI.setWindowSize(width, height);
}, function(error){alert("API initialization failed");}
);
</script>
Use offsetHeight or clientHeight to get div's height:
document.getElementById("fb-root").offsetHeight // including border
document.getElementById("fb-root").clientHeight // excluding border
Use offsetLeft and offsetTop to get div's position relative to its offsetParent. offsetParent is its first parent box which position style is fixed, relative or absolute.