jsPlumb - dynamic endpoint anchors on each side - javascript

I am trying to figure out a way how to add endpoint anchors dynamically to jsPlumb container.
I would like to have source endpoints on the left side and target endpoints on the right side only.
The problem is, that I wasn't able to find any way to do so, without resorting to some hacks, like I am doing now.
jsPlumb supports Continuous Anchors, but position of individual anchor will be recalculated based on the orientation between connectors and number of continuous anchors. This means both source and target endpoints could be sharing the same side of the container, this is something I would like to avoid.
Here is a jsFiddler code I came up with
Here is a part of the code I am using to hack and recalculate anchor positions myself (when Add button is clicked), with some buggy results :(
function fixEndpoints(endpoints) {
//there are 2 types - input and output
var inputAr = $.grep(endpoints, function (elementOfArray, indexInArray) {
return elementOfArray.isSource; //input
});
var outputAr = $.grep(endpoints, function (elementOfArray, indexInArray) {
return elementOfArray.isTarget; //output
});
calculateEndpoint(inputAr, true);
calculateEndpoint(outputAr, false);
}
function calculateEndpoint(endpointArray, isInput) {
//multiplyer
var mult = 1 / endpointArray.length;
for (var i = 0; i < endpointArray.length; i++) {
if (isInput) {
endpointArray[i].anchor.x = 1;
endpointArray[i].anchor.y = mult * i;//, 1, 0] };
}
else {
endpointArray[i].anchor.x = 0;
endpointArray[i].anchor.y = mult * i;//, -1, 0] };
}
}
}
//Add additional anchor
$(".button_add").live("click", function () {
var parentnode = $(this)[0].parentNode.parentNode;
jsPlumb.addEndpoint(
parentnode,
anEndpointSource
);
jsPlumb.addEndpoint(
parentnode,
anEndpointDestination
);
//get list of current endpoints
var endpoints = jsPlumb.getEndpoints(parentnode);
//fix endpoints
fixEndpoints(endpoints);
jsPlumb.recalculateOffsets();
jsPlumb.repaint(parentnode);
});
As you can see on the image above, left side has only source endpoints (Dot) and right side (Box) only target endpoints, once new endpoint is added, anchors are recalculated based on the number of anchors on one side.
This works but still buggy: position is updated only once I move the container and connection between containers is not correct as well.
What I would like to have, is a way for it to work and connect items correctly (preferably using correct jsPlumb code without resorting to hacks)

I finally figured out how to do it. It was easier than I thought.
Code is basically the same with a few changes, here is updated fiddler sample
<!DOCTYPE html>
<html>
<head>
<title>JS plumb test</title>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.23/jquery-ui.min.js"></script>
<script type="text/javascript" src="./include/jquery.jsPlumb-1.3.16-all-min.js"></script>
<style>
.window {
background-color: #EEEEEF;
border: 1px solid #346789;
border-radius: 0.5em;
box-shadow: 2px 2px 5px #AAAAAA;
color: black;
height: 5em;
position: absolute;
width: 5em;
}
.window:hover {
box-shadow: 2px 2px 19px #AAAAAA;
cursor: pointer;
}
.button_add, .button_add_window, .button_remove, .button {
background-color: deepskyblue;
text-align: center;
border: 1px solid;
}
.button_container {
margin: 5px;
background-color: #aaaaaa
}
</style>
<script>
jsPlumb.ready(function () {
//FIX DOM:
$(("#container1"))[0].innerHTML = $(("#container0"))[0].innerHTML;
//all windows are draggable
jsPlumb.draggable($(".window"));
var anEndpointSource = {
endpoint: "Rectangle",
isSource: true,
isTarget: false,
maxConnections: 1,
anchor: [1, 0, 1, 0]
};
var anEndpointDestination = {
endpoint: "Dot",
isSource: false,
isTarget: true,
maxConnections: 1,
anchor: [0, 1, -1, 0]
};
//Fixes endpoints for specified target
function fixEndpoints(parentnode) {
//get list of current endpoints
var endpoints = jsPlumb.getEndpoints(parentnode);
//there are 2 types - input and output
var inputAr = $.grep(endpoints, function (elementOfArray, indexInArray) {
return elementOfArray.isSource; //input
});
var outputAr = $.grep(endpoints, function (elementOfArray, indexInArray) {
return elementOfArray.isTarget; //output
});
calculateEndpoint(inputAr, true);
calculateEndpoint(outputAr, false);
jsPlumb.repaintEverything();
}
//recalculate endpoint anchor position manually
function calculateEndpoint(endpointArray, isInput) {
//multiplyer
var mult = 1 / (endpointArray.length+1);
for (var i = 0; i < endpointArray.length; i++) {
if (isInput) {
//position
endpointArray[i].anchor.x = 1;
endpointArray[i].anchor.y = mult * (i + 1);
}
else {
//position
endpointArray[i].anchor.x = 0;
endpointArray[i].anchor.y = mult * (i + 1);
}
}
}
//Add additional anchor
$(".button_add").live("click", function () {
var parentnode = $(this)[0].parentNode.parentNode;
jsPlumb.addEndpoint(
parentnode,
anEndpointSource
);
jsPlumb.addEndpoint(
parentnode,
anEndpointDestination
);
fixEndpoints(parentnode);
});
//Remove anchor
$(".button_remove").live("click", function () {
var parentnode = $(this)[0].parentNode.parentNode;
//get list of current endpoints
var endpoints = jsPlumb.getEndpoints(parentnode);
//remove 2 last one
if (endpoints.length > 1) {
jsPlumb.deleteEndpoint(endpoints[endpoints.length - 2]);
}
if (endpoints.length > 0) {
jsPlumb.deleteEndpoint(endpoints[endpoints.length - 1]);
}
fixEndpoints(parentnode);
});
//adds new window
$(".button_add_window").click(function () {
var id = "dynamic_" + $(".window").length;
//create new window and add it to the body
$('<div class="window" id="' + id + '" >').appendTo('body').html($(("#container0"))[0].innerHTML);
//set jsplumb properties
jsPlumb.draggable($('#' + id));
});
});
</script>
</head>
<body >
<!-- Adds new windows to the page -->
<div class="window" style="left: 600px" id="details">
<p style="text-align: center">Window</p>
<div class="button_container">
<div class="button_add_window">Add</div>
</div>
</div>
<!-- Primary window - used as html templated for descendants -->
<div class="window" style="left: 20px" id="container0">
<div class="button_container">
<div class="button_add">Add</div>
<div class="button_remove">Remove</div>
</div>
</div>
<div class="window" style="left: 200px" id="container1">
</div>
</body>
</html>
Changes that I made:
Now I specify endpoint anchor offset when I add it, I only calculate anchor position, so offset never changes, it is always correct from the start:
var anEndpointSource = {
endpoint: "Rectangle",
isSource: true,
isTarget: false,
maxConnections: 1,
anchor: [1, 0, 1, 0]
};
Once endpoint is added, I re-calculate anchor positions and call (this will repaint connections):
jsPlumb.repaintEverything();
Here is the final result:

You can delete states by double click by adding
newState.dblclick(function(e) {
alert("This will delete the state and its connections");
instance.detachAllConnections($(this));
$(this).remove();
e.stopPropagation();
});
function to your jsPlumb.ready function. you can add this to your all states by adding
var windows = jsPlumb.getSelector(".statemachine-demo .state");
windows.dblclick(function(e) {
alert("This will delete the state and its connections");
instance.detachAllConnections($(this));
$(this).remove();
e.stopPropagation();
});
here statemachine-demo is id of div in your contaner and state is class of state divs.

thank you for the solution, it works fine when the anchor side is predefined, like here sources are always on the left side and targets are always on the right.
But if they are dynamic we need to implement the side selection also by ourselves?
As a workaround what I did is to set more possible anchor positions in default config. Any better idea?
thanks

Related

How to move nodes from a contentEditable div to another dynamically when its content exceed a x height?

I am trying to prototype a simple wyswyg that emulate the concept of A4 pages using contentEditable divs.
So my current code is this:
HTML:
<div id="editor">
<div contenteditable="true" class="page" id="page-1">
<b>hello</b>
</div>
</div>
CSS:
#editor{
background-color: gray;
border: 1px black;
padding: 1em 2em;
}
.page{
background-color: white;
border: solid black;
padding: 1em 2em;
width:595px;
height:841px;
word-wrap: break-word;
overflow-wrap: break-word;
white-space: normal;
}
JS:
//force br
document.execCommand("DefaultParagraphSeparator", false, "br");
const a4 = {
height: 841,
width: 595
};
document.getElementById('editor').addEventListener('input', function(e) {
let getChildrenHeight = function(element) {
total = 0;
if (element.childNodes) {
for (let child of element.childNodes) {
switch (child.nodeType) {
case Node.ELEMENT_NODE:
total += child.offsetHeight;
break;
case Node.TEXT_NODE:
let range = document.createRange();
range.selectNodeContents(child);
rect = range.getBoundingClientRect();
total += (rect.bottom - rect.top);
break;
}
}
}
return total;
};
let pages = document.getElementsByClassName('page');
for (let i in pages) {
let page = pages[i];
//remove empty page
if (page.offsetHeight == 0 && i > 1) {
page.remove();
}
let childrenHeight = getChildrenHeight(page);
while (childrenHeight > a4.height) {
//recursively try to fit elements on max size
//removing/pushing excedents elements to the next div (aka page)
let excedents = [];
let children = page.childNodes;
let children_length = children.length - 1;
let backup = children[children_length].cloneNode(true);
children[children_length].remove();
if (pages.item(i + 1) === null) {
var newPage = page.cloneNode(true);
newPage.innerHTML = '';
newPage.appendChild(backup);
page.parentNode.appendChild(newPage);
} else {
page.item(i + 1).insertBefore(backup, page.item(i + 1).childNodes[0]);
}
//console.log(children[i].innerHTML);
}
}
});
Unfortunately, the result is not as I was expecting.
When the height of one page is exceeded, all content from the first page is removed, not like I would like:
the excess to be moved to next page.
and when a page is abscent of children, been removed.
Something like a very very primitive Microsoft Word multipages editor.
How to do that?
Thanks in advance
Celso
Your code is a good start, but there are a couple off things to fix:
You are a trying to iterate trough a HTMLCollection with your for..in loop, which will access length, item and namedItem in the collection (just try for(let i in document.getElementsByClassName('page')) console.log(i); in the console)
You're trying to remove empty pages when offsetHeight is 0, instead try childrenHeight
you can exchange the while loop with an if statement
you also have to check if there is enough sapce on the current page, to pull back lines from the next one
also, you have to manually handle cursor position on page breaks
I made a codepen to demonstrate the changes I suggested. It is far from perfect, but handles page removals and excess removal.

Dynamically alter contents of a div and append to another div

So basically I have this div in body
<body>
<div id="main_content"></div>
</body>
Now I am downloading some data from the internet (a set of boolean data) and I have this div template. Let's say the data is (true, false, true). Then for each data I want to alter the template div. For example: first one is true so inside the template div I will change the sub1 div's height to 40 px; if it's false, I'd change sub2 div's height to 40 px; and then I'd append this modified template div to main_content div
Template div:
.child{
width:300px;
height:auto;
}
.sub1{
width:300px;
height:20px;
background-color:#0FF;
}
.sub2{
width:300px;
height:20px;
background-color:#F0F;
}
<div class="child">
<div class="sub1"></div>
<div class="sub2"></div>
</div>
After all this this should be the final output of main_content div
What would be the easiest way of doing this using HTML/CSS/JS.
Thanks
Short answer: Here is a codepen
Long answer:
I would use js to dynamically generate your template div:
function makeTemplateDiv() {
var child = document.createElement('div');
child.className = "child"
var sub1 = document.createElement('div');
sub1.className = "sub1"
var sub2 = document.createElement('div');
sub2.className = "sub2"
child.appendChild(sub1);
child.appendChild(sub2);
return child;
}
Then make a css class for a taller 40 px
.taller {
height: 40px;
}
Then use js to to alter your template based on a passed in value
function alterTemplateDiv(value) {
var template = makeTemplateDiv();
if(value) {
template.getElementsByClassName("sub1")[0].className += " taller";
} else {
template.getElementsByClassName("sub2")[0].className += " taller";
}
return template;
}
Then use js to pass in your array of values, make the divs, and append them
function appendDivs(arrayOfValues) {
var mainDiv = document.getElementById("main_content");
for(var i = 0; i < arrayOfValues.length; i++) {
mainDiv.appendChild(alterTemplateDiv(arrayOfValues[i]));
}
}
This kind of question begs for a million different types of answers, but I think this generally keeps with most best practices for front end coding without the use of a framework:
// Self-invoking function for scoping
// and to protect important global variables from other script changes
// (The variable references can be overwritten)
(function (window, document) {
var templateText,
generatedEl,
topEl,
bitArray;
// Data
bitArray = [true, false, true];
// Get template text
templateText = document.getElementById('my-template').text.trim();
// Loop through your T / F array
for (var i = 0, l = bitArray.length; i < l; i++) {
// Create a DIV and generate HTML within it
generatedEl = document.createElement('div');
generatedEl.innerHTML = templateText;
// Modify the new HTML content
topEl = generatedEl.getElementsByClassName('child')[0];
topEl.className += bitArray[i] ? ' typeA' : ' typeB' ;
// Insert generated HTML (assumes only one top-level element exists)
document.getElementById('my-container').appendChild(generatedEl.childNodes[0]);
}
})(window, document);
.child {
width: 300px;
height: auto;
}
/* For true */
.child.typeA > .sub1 {
width: 300px;
height: 40px;
background-color: #0FF;
}
.child.typeA > .sub2 {
width: 300px;
height: 20px;
background-color: #F0F;
}
/* For false */
.child.typeB > .sub1 {
width: 300px;
height: 20px;
background-color: #0FF;
}
.child.typeB > .sub2 {
width: 300px;
height: 40px;
background-color: #F0F;
}
<!-- Container -->
<div id="my-container">
<!-- HTML Template -->
<script id="my-template" type="text/template">
<div class="child">
<div class="sub1"></div>
<div class="sub2"></div>
</div>
</script>
</div>
Note that the HTML content, JavaScript code and CSS are all kept very separated. This is based on the concepts of "Separation of Concerns" and "Unobtrusive JavaScript". I invite you to read up on them if you haven't already. Also, front end templating can be used for dynamic content like I did here, but I would recommend doing templating on the back end when you can. It works better for SEO purposes.
jQuery makes it easier to manipulate the DOM, so here is another solution for your problem:
var data = [true, false, true];
for (i=0; i<data.length; i++) {
var height1;
var height2;
if (data[i] == true) {
height1 = 40;
height2 = 20;
}
else {
height1 = 20;
height2 = 40;
}
var div1 = document.createElement("div");
$(div1).toggleClass("sub1")
.height(height1)
.appendTo("#main_content");
var div2 = document.createElement("div");
$(div2).toggleClass("sub2")
.height(height2)
.appendTo("#main_content");
}

DIV stick to top till pushed offscreen by next DIV

I am trying to get a similar effect to the Instagram timeline on a project I am doing. My HTML is:
<div class="item">
<div class="title">
<h1>Some title</h1>
<span>Time here</span>
</div>
<div class="content"></div>
</div>
Which is repeated many times down the page, and my CSS is:
* {
margin: 0;
padding: 0;
}
.item {
width: 100%;
background-color: red;
}
.item h1, span{
padding: 2px 0 5px 5px;
}
.item span{
display: inline;
}
.title {
background-color: blue;
}
.content {
height: 200px;
background-color: green;
}
What I want to happen is when a user scrolls, <div class="title"> sticks to the top when a user is scrolling, but when the next <div class="title"> comes up it 'pushes' the previous one off screen and then fixes it's self to the top.
Screenshots:
Picture 1 - Look at the two headers, one for withhearts the other for brenton_clarke.
Picture 2 - brenton_clarke's header has reached the bottom of withheart's
Picture 3 - brenton_clarke's header is pushing withheart's offscreen
Picture 4 - brenton_clarke's headers is now stuck to the top till pauloctavious pushes it off
My Fiddle
Can anyone give me some help with this?
With the link suggested below I was able to get it sort-of working, but not great: http://jsfiddle.net/reb6X/1/
Having modified the jQuery to use .html() rather than .text() it works not to badly now: http://jsfiddle.net/reb6X/2/
You might want to check out this jsfiddle.
http://jsfiddle.net/kennis/JTvFZ/
I think it could get you in the right direction.
// Index of the currently 'active' section
var activeCache = null;
// Actual rendered height of a header element
var cloneHeight = function(){
var $clone = $('<div class="clone"></div>').appendTo('body'),
cloneHeight = $clone.outerHeight();
$clone.remove();
return cloneHeight;
}();
// Top offsets of each header
var offsets = [];
// Figure out which section is 'active'
var activeHeaderIndex = function(){
var scrollTop = document.body.scrollTop;
for ( var i = 0; i < offsets.length; i++ )
if ( offsets[i] - cloneHeight > scrollTop )
return Math.max( i - 1, 0 );
}
// Build the 'offsets' array
$('.header').each(function(i, obj){
offsets.push( $(this).offset().top );
});
// Listen to scroll events
$(window).on('scroll', function(){
var active = activeHeaderIndex(),
scroll = document.body.scrollTop,
clone = $('.clone').length,
$active = $('.header').eq(active),
prevTitle = $('.header').eq(active - 1).text(),
title = $active.text(),
$fixed = $('.fixed');
// Hide fixed header
if ( offsets[active] > scroll ){
if ( !clone ){
$('.header').eq(0).hide();
$('<li class="clone">' + prevTitle + '</li>').insertBefore($active);
}
$fixed.hide();
// Show fixed header
} else {
if ( clone ){
$('.header').eq(0).show();
$('.clone').remove();
}
$fixed.show();
}
// If we're not changing headers, exit
if ( active == activeCache ) return;
// Update active index
activeCache = active;
// Remove old fixed header (if any)
$('.fixed').remove();
// Add a new fixed header
$fixed = $('<div class="fixed">' + title + '</div>').appendTo('body');
}).trigger('scroll');

jQuery/JavaScript collision detection

How to detect if two <div> elements have collided?
The two divs are simple coloured boxes travelling perpendicular to each other, so no complicated shapes or angles.
var overlaps = (function () {
function getPositions( elem ) {
var pos, width, height;
pos = $( elem ).position();
width = $( elem ).width();
height = $( elem ).height();
return [ [ pos.left, pos.left + width ], [ pos.top, pos.top + height ] ];
}
function comparePositions( p1, p2 ) {
var r1, r2;
r1 = p1[0] < p2[0] ? p1 : p2;
r2 = p1[0] < p2[0] ? p2 : p1;
return r1[1] > r2[0] || r1[0] === r2[0];
}
return function ( a, b ) {
var pos1 = getPositions( a ),
pos2 = getPositions( b );
return comparePositions( pos1[0], pos2[0] ) && comparePositions( pos1[1], pos2[1] );
};
})();
$(function () {
var area = $( '#area' )[0],
box = $( '#box0' )[0],
html;
html = $( area ).children().not( box ).map( function ( i ) {
return '<p>Red box + Box ' + ( i + 1 ) + ' = ' + overlaps( box, this ) + '</p>';
}).get().join( '' );
$( 'body' ).append( html );
});
body {
padding: 30px;
color: #444;
font-family: Arial, sans-serif;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
#area {
border: 2px solid gray;
width: 500px;
height: 400px;
position: relative;
}
#area > div {
background-color: rgba(122, 122, 122, 0.3);
position: absolute;
text-align: center;
font-size: 50px;
width: 60px;
height: 60px;
}
#box0 {
background-color: rgba(255, 0, 0, 0.5) !important;
top: 150px;
left: 150px;
}
#box1 {
top: 260px;
left: 50px;
}
#box2 {
top: 110px;
left: 160px;
}
#box3 {
top: 200px;
left: 200px;
}
#box4 {
top: 50px;
left: 400px;
}
p {
margin: 5px 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<h1>Detect overlapping with JavaScript</h1>
<div id="area">
<div id="box0"></div>
<div id="box1">1</div>
<div id="box2">2</div>
<div id="box3">3</div>
<div id="box4">4</div>
</div>
General idea - you get the offset and dimension of the boxes and check whether they overlap.
If you want it to update, you can use setInterval:
function detectOverlapping() {
// code that detects if the box overlaps with a moving box
setInterval(detectOverlapping, 25);
}
detectOverlapping();
Also, note that you can optimize the function for your specific example.
you don't have to read the box dimensions repeatedly (like I do in my code) since they are fixed. You can read them on page load (into a variable) and then just read the variable
the horizontal position of the little box does not change (unless the user resizes the window). The vertical positions of the car boxes does not change. Therefore, those values also do not have to be read repeatedly, but can also be stored into variables.
you don't have to test whether the little box overlaps with all car boxes at all times. You can - based on its vertical position - figure out in which lane the box is currently, and test only the specific car box from that lane.
I believe this is the easiest way:
https://plugins.jquery.com/overlaps/
Here is another one, in German:
http://www.48design.de/news/2009/11/20/kollisionsabfrage-per-jquery-plugin-update-v11-8/
I'd give those a try.
--UPDATE--
I can't really spend anytime on it right now, but i can when i get home if no one answers but you;d do something like:
setInterval(function(){
//First step would be to get the offset of item 1 and item 2
//Second would be to get the width of each
//Third would be to check if the offset+width ever overlaps
//the offset+width of the 2nd
//Fourth would be, if so, do X or set a class...
},10);
Its a little late on this but I guess you could use this approach that I tried when I was faced with the similar situation. The advantage here is that there are no additional plugin, or scripts involved and neither do you have to introduce performance hungry polling into it.
This technique uses the the built-in methods and events that Jquery's droppable has to offer.
Ok, enough said, here's the solution technique:
Say if you have two elements (images in my case) and you don't want them to overlap or detect when they do, make the two elements a droppable and make them to 'accept' each other:
$([div1, div2]).droppable(CONFIG_COLLISSION_PREVENTION_DROPPABLE);
The 'CONFIG_COLLISSION_PREVENTION_DROPPABLE' looks like this:
var originatingOffset = null;
CONFIG_COLLISSION_PREVENTION_DROPPABLE = {
tolerance: "touch",
activate : function (event, ui) {
// note the initial position/offset when drag starts
// will be usedful in drop handler to check if the move
// occurred and in cae overlap occurred, restore the original positions.
originatingOffset = ui.offset;
},
drop : function (event, ui) {
// If this callback gets invoked, the overlap has occurred.
// Use this method to either generate a custom event etc.
// Here, i used it to nullify the move and resetting the dragged element's
// position back to it's original position/offset
// (which was captured in the 'activate' handler)
$(ui.draggable).animate({
top: originatingOffset.top + "px",
left: originatingOffset.left + "px"
}, 300);
}
}
The 'activate' and 'drop' handlers refer to the 'dropactivate' and 'drop' events of "droppable" plugin
Here, the key is the 'drop' callback. Whenever any of the two elements overlap and they are dropped over each other, the 'drop' will be called. This is the place to detect and take actions, may be sending out custom events or calling other actions (I here chose to revert the overlapping element's positions to the initial position when the drag started, which was captured in 'activate' callback).
That's it. No polling, no plugins, just the built-in events.
Well, there can be other optimizations/extensions done to it, this was simply the first shot out of my head that worked :)
You can also use the 'dropover' and 'dropout' events to signal and create a visual feedback to the user that two elements are overlapping, while they may be still on the move.
var CLASS_INVALID = "invalid";
// .invalid { border: 1px solid red; }
...
$.extend(CONFIG_COLLISSION_PREVENTION_DROPPABLE, {
over : function (event, ui) {
// When an element is over another, it gets detected here;
// while it may still be moved.
// the draggable element becomes 'invalid' and so apply the class here
$(ui.draggable).addClass(CLASS_INVALID);
},
out : function(event, ui) {
// the element has exited the overlapped droppable now
// So element is valid now and so remove the invalid class from it
$(ui.draggable).removeClass(CLASS_INVALID);
}
});
Hope this helps!
You can do this using getBoundingClientRect()
function isOverlapping(div1, div2){
const div1 = div1.getBoundingClientRect();
const div2 = div2.getBoundingClientRect();
return (div1.right > div2.left &&
div1.left < div2.right &&
div1.bottom > div2.top &&
div1.top < div2.bottom)
}
EDIT: I have written a blog post on my website. Here a link to it.
http://area36.nl/2014/12/creating-your-own-collision-detection-function-in-javascript/
Well I had the same problem but thanks to the answer of Oscar Godson I got a function that works. I used Jquery for easy coding and because i'm lazy ;p. I put the function in a other function that is fired every second so keep that in mind.
function collidesWith (element1, element2) {
var Element1 = {};
var Element2 = {};
Element1.top = $(element1).offset().top;
Element1.left = $(element1).offset().left;
Element1.right = Number($(element1).offset().left) + Number($(element1).width());
Element1.bottom = Number($(element1).offset().top) + Number($(element1).height());
Element2.top = $(element2).offset().top;
Element2.left = $(element2).offset().left;
Element2.right = Number($(element2).offset().left) + Number($(element2).width());
Element2.bottom = Number($(element2).offset().top) + Number($(element2).height());
if (Element1.right > Element2.left && Element1.left < Element2.right && Element1.top < Element2.bottom && Element1.bottom > Element2.top) {
// Do your stuff here
}
}
What it does is basically it gets all the values of element1 and then get all the values of element2. Then with the help of some calculations it figures out all the values. Then in the if statement it compares the square of element1 to the square of element2. If the values of element1 are between the left, right, top and bottom values of element2. If that is true the code in the bottom is executed.
I ran into this generalized issue myself, so (full disclosure) I made a plugin for it. For simple collision queries about static objects, try this:
http://sourceforge.net/projects/jquerycollision/
Which allows you to get a list of overlapping collision boxes (or none if there's no collision):
hits = $("#collider").collision(".obstacles");
Or to get a collision event during "dragging", use this:
http://sourceforge.net/apps/mediawiki/jquidragcollide/?source=navbar#collision
Which gives you a "collision" event to connect to. (Or a "protrusion" event, to see if a div escapes another div that currently contains it.)
$(draggable).bind(
"collision",
function(event,ui) {
...
}
);
If you are checking collisions during motion other than dragging, just call the original repeatedly, it's pretty quick. Note: the dragging one doesn't play nicely with resizing.
Post is old, May be it help someone...
function CheckDiv()
{
var ediv1 = document.getElementById('DIV1');
var ediv2 = document.getElementById('DIV2');
ediv1.top = $(ediv1).offset().top;
ediv1.left = $(ediv1).offset().left;
ediv1.right = Number($(ediv1).offset().left) + Number($(ediv1).width());
ediv1.bottom = Number($(ediv1).offset().top) + Number($(ediv1).height());
ediv2.top = $(ediv2).offset().top;
ediv2.left = $(ediv2).offset().left;
ediv2.right = Number($(ediv2).offset().left) + Number($(ediv2).width());
ediv2.bottom = Number($(ediv2).offset().top) + Number($(ediv2).height());
if (ediv1.right > ediv2.left && ediv1.left < ediv2.right && ediv1.top < ediv2.bottom && ediv1.bottom > ediv2.top)
{
alert("hi");
}
if (ediv1.left > ediv2.left && ediv1.top > ediv2.top && ediv1.right < ediv2.right && ediv1.bottom < ediv2.bottom)
{
alert("hello");
}
}

YUI skinning scrollbar used in container

How can I implement custom-looking scrollbars in YUI containers?
Using YUI3, you can change the scroll bars of an internal frame. If you ment the external scroll bars of the actual browser -- just drop the idea. It is not worth any headache. Too many browsers simply won't let you.
Here is example of an internal setup skinned in YUI 3.4
CSS:
<style type="text/css" media="screen">
#video-playlist-scroll-bar-container{text-align:right}
#video-playlist-scroll-bar{position:relative;width:14px;border:1px solid #e0e0e0;height:379px;margin:0 auto}
#drag-handle-container-wrap{position:relative;top:17px;bottom:17px;left:0;width:14px;height:345px}
#drag-handle-container-wrap .yui3-slider-content{position:absolute;top:0;left:0}
#drag-handle-draggable{position:absolute;left:0;background-color:#eaeaea;text-align:center;cursor:move;width:14px}
#drag-handle-up,#drag-handle-down{position:absolute;display:block;width:14px;height:16px;cursor:pointer}
#drag-handle-up{top:0;left:0}
#drag-handle-down{bottom:0;left:0}
</style>
HTML:
<div class="yui3-u-1-12" id="video-playlist-scroll-bar-container">
<div id="video-playlist-scroll-bar">
<div id="drag-handle-up"><img src="/assets/images/rebrand/drag-arrow-top.gif"></div>
<div id="drag-handle-container-wrap">
<span class="yui3-widget yui3-sliderbase yui3-slider" style=""><span class="yui3-slider-content yui3-slider-y"><div id="drag-handle-container" style="height: 345px; ">
<div id="drag-handle-draggable" class="yui3-dd-draggable" style="top: 0px; left: 0px; ">
<img src="/assets/images/rebrand/drag-handle.gif" width="9" height="100">
</div></div></span></span></div>
<div id="drag-handle-down"><img src="/assets/images/rebrand/drag-arrow-bottom.gif"></div>
</div>
</div>
YUI:
YUI().use("base","node",'slider',function(Y){
var CLICK = "click",
ID = "#",
_scrollingBuffer = 75,
_maxScrollRegion = 0;
// Slider
var _tmp = Y.one(ID+'playlist-container'+" .video-playlist-item"),
_nodeBuffer = Y.one(ID+'playlist-container').get('children').slice(-5),
_bufferFunction = function() {
var _height = 0;
_nodeBuffer.each(function(i) {
_height = _height + i.get('region').height;
});
return _height;
},
_buffer = _bufferFunction(),
_maxScrollRegion = Y.one(ID+'playlist-container').get("region").height - _buffer;
var listScroll = new Y.Slider({
axis : 'y',
min : 0, // reverse min and max to make the top
max : _maxScrollRegion,
value : 0,
length: '345px'
});
listScroll.renderRail = function () {
return Y.one( "#drag-handle-container" );
};
listScroll.renderThumb = function () {
return this.rail.one( "#drag-handle-draggable" );
};
listScroll.render( "#drag-handle-container-wrap" );
listScroll.on('valueChange', Y.bind(function (e) {
//scroll something?
}));
Y.one("#drag-handle-up").on(CLICK, Y.bind(function (e) {
e.preventDefault();
if (listScroll.get('value') >= _scrollingBuffer) {
listScroll.setValue(listScroll.get('value') - _scrollingBuffer);
} else {
listScroll.setValue(0);
}
}));
Y.one("#drag-handle-down").on(CLICK, Y.bind(function (e) {
e.preventDefault();
if (listScroll.get('value') <= Math.round(_maxScrollRegion - _scrollingBuffer)) {
listScroll.setValue(listScroll.get('value') + _scrollingBuffer);
} else {
listScroll.setValue(_maxScrollRegion);
}
}));
});
Note, this is pretty much a copy/paste from a project of mine -- with a quick removal of any identifiers. It may not work as a copy/paste .. but you'll get the jist.
Final product:
You could go even further by not using a "overflow: auto;" CSS and use the YUI 3 ScrollView along with it. I use the Paged version on YUI myself. It is easy enough to do a up & down by page & percentages.
Hopefully this is what you were looking for, not the browser's scroll bar.

Categories

Resources