I am trying to build a menu which detects hover states and hide/shows relevant divs depending on where the mouse is. I need to do this with Prototype.js.
The menu looks something like this :
========ONE========TWO======THREE=======FOUR======FIVE======
============================================================
------------------- BIG MIDDLE DIV -------------------------
============================================================
============================================================
-------------------- TARGET HIDDEN DIV ---------------------
============================================================
When you mouse over link one,two,three.. it will show the related target div. The trick is when you mouseout, it needs to keep that div visible if you are on the middle div or the active state div. If you mouseout anywhere else in the body it needs to hide. Here is updated code based off the answer so far.
<ul><li #id="one">one</li><li>two</li><li>three</li><li>four</li></ul>
<div id="middleBar"></div>
<div id="container-one">1</div>
<div id="container-two">2</div>
<div id="container-three">3</div>
<div id="container-four">4</div>
<script>
MouseOff = true;
function getTarget(event) {
var el = event.target || event.srcElement;
return el.nodeType == 1? el : el.parentNode;
}
var ShowDiv = function(activeDiv){
$(activeDiv).addClassName('isActive');
var activeSibs = $(activeDiv).siblings();
activeSibs.invoke('removeClassName', 'isActive');
};
var HideDiv = function(){
if(MouseOff){
$$('div').invoke('removeClassName','isActive');
}
};
$$('li').invoke('observe','mouseenter',function(){
console.log(getTarget(event));
MouseOff = false;
var linkName = this.innerHTML;
var activeDiv = 'container-' + linkName;
ShowDiv(activeDiv);
});
$$('li').invoke('observe','mouseleave',function(){
MouseOff = true;
HideDiv();
});
$$('#middleBar').invoke('observe','mouseenter',function(){
console.log(getTarget(event));
MouseOff = false;
});
</script>
Here is a fiddle of this :
http://jsfiddle.net/TqMtK/5/
To further clarify what I am trying to achieve, once the div is activated it needs to stay visible while on that nav trigger, the middle bar, or the active div itself. Something else I was thinking was to use that getTarget function to always check what element the mouse is above, but this just feels wrong to me and does not seem very efficient. Any opinions on that?
UPDATE : Still trying to work through this.. now I am a little closer and the flag is set correctly when over middle div, but when it goes over the active div it resets the flag and the div disappears. I tried adding back a timeout.. here is latest attempt :
http://jsfiddle.net/TqMtK/7/
UPDATE : Ok I think I might have this, at this point I would like to just hear any feedback on this solution. I found that because the active class is being added dynamically the observer method must be included in the function that creates it : ShowDiv. Here is what I got :
http://jsfiddle.net/TqMtK/9/
UPDATED: tues night. I am sure this can be more succinct. Perhaps it is just my browser but I notice that I can only mouse over Panel1 and show that it doesn't disappear, the other Panels (because of their positioning) leave a space which is the "body" and I close on that. Hopefully this is a bit better.
http://jsfiddle.net/TqMtK/12/
var tabPanels = {
options : {
activePanel : "",
activeTab : ""
},
showPanel : function(panel){
this.hidePanel();
this.options.activePanel = 'container-' + panel;
this.options.activeTab = panel;
$(this.options.activePanel).addClassName('isActive').setAttribute('panel',panel);
},
hidePanel : function(panel){
if(Object.isElement($(panel)) && $(panel).hasAttribute('panel') ){
if($(panel).readAttribute('panel') == this.options.activeTab ){
return;
}else{
if(!this.options.activePanel.blank()){
$(this.options.activePanel).removeClassName('isActive');
}
}
}else{
if(!this.options.activePanel.blank()){
$(this.options.activePanel).removeClassName('isActive');
}
}
}
}
document.observe('mouseover', function(e){
switch(e.target.id){
case 'middleBar':
break;
case 'one':
case 'two':
case 'three':
case 'four':
tabPanels.showPanel(e.target.id);
break;
default:
tabPanels.hidePanel(e.target.id);
}
})
After much tinkering I finally found a working solution for this. Thanks to the idea from james I went the route of setting a flag depending on which element the mouse was over. That combined with a timeout allowed me to keep running checks on the mouse location. Part of my problem was where I was invoking the observe event on the class that was dynamically added from ShowDiv(), when I moved it in to that function I was able to get it to work. Here is the js I ended up with
MouseLocator = 'off';
var ShowDiv = function(activeDiv){
$(activeDiv).addClassName('isActive');
var activeSibs = $(activeDiv).siblings();
activeSibs.invoke('removeClassName', 'isActive');
$$('.isActive').invoke('observe','mouseenter',function(){
MouseLocator = 'on';
});
$$('.isActive').invoke('observe','mouseleave',function(){
MouseLocator = 'off';
setTimeout(function(){HideDiv()},500);
});
};
var HideDiv = function(){
if(MouseLocator == 'off'){
$$('div').invoke('removeClassName','isActive');
}
};
$$('li').invoke('observe','mouseenter',function(){
MouseLocator = 'on';
var linkName = this.innerHTML;
var activeDiv = 'container-' + linkName;
ShowDiv(activeDiv);
});
$$('li').invoke('observe','mouseleave',function(){
MouseLocator = 'off';
setTimeout(function(){HideDiv()},500);
});
$$('#middleBar').invoke('observe','mouseenter',function(){
MouseLocator = 'on';
});
$$('#middleBar').invoke('observe','mouseleave',function(){
MouseLocator = 'off';
setTimeout(function(){HideDiv()},500);
});
and a fiddle : http://jsfiddle.net/TqMtK/9/
I guess this is resolved but I am always open to suggestions on how to improve my code :)
Related
I'm trying to create a fairly simple script which uses links to toggle which div is being displayed. There is a div for each day, and when a corresponding link is clicked, it shows the according div and hides the others.
This works, but once you release the mouse it reverts back to the default state. I'm guessing this is something very simple, but I've tried searching around and can't find anything.
Here is the code I'm using:
Fiddle
$(document).ready(function() {
var dayDivs = [];
var displayDay = 0;
loadDayDivs();
adjustDayDisplay();
$('.day-link').mousedown(function() {
var linkClicked = $(this).text();
switch (linkClicked) {
case "Friday":
displayDay = 0;
break;
case "Saturday":
displayDay = 1;
break;
case "Sunday":
displayDay = 2;
break;
}
adjustDayDisplay();
});
function loadDayDivs() {
dayDivs[0] = $(".friday-div");
dayDivs[1] = $(".saturday-div");
dayDivs[2] = $(".sunday-div");
}
function adjustDayDisplay() {
for (var i = 0; i < dayDivs.length; i++) {
dayDivs[i].css("cssText", "display: none !important;");
}
dayDivs[displayDay].css("cssText", "display: inline !important;");
}
});
on jsfiddle I get a strange error which seems to be associated with forms normally. Any help on this would be VERY much appreciated.
You should change the mouse down event to click event and suppress the click event.
This fiddle works
$('.day-link').click(function(e) {
e.preventDefault(); // New line
You need to use click event and prevent default behavior using event.preventDefault(), otherwise the browser will actually open the link. Here is an updated fiddle - https://jsfiddle.net/yLrhrumx/2/
I'm building a function in my own image browser that creates and displays a delete button when the user hovers the cursor over a certain image's div and hides the button when the user hover the mouse out of the div.
this is the code:
function displayImages() {
//run through array of images
for (a = 0; a < images.length; a++) {
var container = document.createElement('div');
container.id = 'container'+images[a];
container.style.width = 120;
container.style.backgroundColor = '#e9e9e9';
container.style.height = 140;
container.style.margin = 5;
container.style.display = 'inline-block';
container.style.float = 'left';
var imageID = images[a];
container.onmouseover = function() {imageRollover(this)};
container.onmouseout = function() {imageMouseOut(this)};
}
}
function imageRollover(image) {
var trashcan = document.createElement('BUTTON');
trashcan.id = 'trash'+image.id;
trashcan.className = 'deleteButton';
trashcan.onclick = function() {deleteImage(image.id);};
document.getElementById(image.id).appendChild(trashcan);
}
function imageMouseOut(image) {
document.getElementById(image.id).removeChild(document.getElementById('trash'+image.id));
}
function deleteImage(image) {
alert(image);
}
My problem is, that when I click trashcan, it calls nothing. I already tried to add the onlick event normally:
trashcan.onclick = deleteImage(image.id);
But then, for some reason, is calls the function when I hover my mouse over the container.
How do I make sure that on click events for dynamically added rollover buttons work?
The function can de viewed on: http://www.imaginedigital.nl/CMS/Editor/ImagePicker.html or http://jsfiddle.net/f239ymos/
Any help would be highly appreciated.
You are forcing me to guess here(not giving a fiddle), and create a scenario of my own but from what I understand, you want a button to appear no hover, and when pressed to delete the image so here its a working fiddle
hover functionality
$((document.body).on('mouseenter', '.test', function(){
console.log('here');
$(this).children('.test .x_butt').show();
});
$(document.body).on('mouseleave', '.test', function(){
$('.test .x_butt').hide();
});
remove functionality
$(document.body).on('click', '.x_butt', function(){
$(this).parent().remove();
});
P.S. as for your dynamically added divs issue, the $(selector1).on('click','selector2', function() {...}); deals with it, as long as selector1 is not added dynamically. (selector2 would be the div you want the function to be on) demo with dynamically added elements ( click clone )
First change
window.onload = loadImages(); to window.onload = loadImages;
Then since you pass an object you can change
function imageMouseOut(image) {
document.getElementById(image.id).removeChild(document.getElementById('trash'+image.id));
}
to
function imageMouseOut(image) {
image.removeChild(image.childNodes[0]);
}
However why not just hide and show the trashcan? Much cleaner
I'm currently making an iphone webapp and have almost finished it, I just need to fix this one little issue im having
Ive managed to hide one div layer and show another, but what I would like is for the same button to then show the layer I have hid and hide the one that I have shown when clicked again. So basically clicking the button would take it back to the original state
the code I am currently using is
<script type="text/javascript">
function toggle_layout(d)
{
var onediv = document.getElementById(d);
var divs=['Posts','Posts2'];
for (var i=0;i<divs.length;i++)
{
if (onediv != document.getElementById(divs[i]))
{
document.getElementById(divs[i]).style.display='none';
}
}
onediv.style.display = 'block';
}
</script>
It hides a div I have named "Posts" and shows a div I have named "Posts2", but clicking it again does not reverse the effect.
If you wanna take a look at my site its http://a-m-creativecapture.tumblr.com/
Will have to view it on a mobile to see what I am talking about.
Have you tried style.visibility (visible|hidden) instead of style.display?
Assuming that your divs are something like this:
<div id="posts"></div>
<div id="posts2"></div>
You can use the following code:
var posts = document.getElementById('posts'),
posts2 = document.getElementById('posts2');
function toggle() {
if (this == posts) {
posts.style.display = 'none';
posts2.style.display = 'block';
} else {
posts.style.display = 'block';
posts2.style.display = 'none';
}
}
div1.onclick = toggle;
div2.onclick = toggle;
I'm using arbor.js to create a graph.
How do I create an onclick event for a node, or make a node link somewhere upon being clicked?
The Arborjs.org homepage has nodes which link to external pages upon being clicked, how do I replicate this, or make the node call javascript function upon being clicked?
My current node and edges listing is in this format:
var data = {
nodes:{
threadstarter:{'color':'red','shape':'dot','label':'Animals'},
reply1:{'color':'green','shape':'dot','label':'dog'},
reply2:{'color':'blue','shape':'dot','label':'cat'}
},
edges:{
threadstarter:{ reply1:{}, reply2:{} }
}
};
sys.graft(data);
A bit of context:
I'm using arbor.js to create a graph of thread starters and replies on my forum.
I've got it working so that id's are displayed 'in orbit' around their respective thread starter.
The reference on the arbor site is really not very helpful.
Any help is much appreciated.
If you look at the atlas demo code (in github) you will see towards the bottom there are a selection of mouse event functions, if you look at:
$(canvas).mousedown(function(e){
var pos = $(this).offset();
var p = {x:e.pageX-pos.left, y:e.pageY-pos.top}
selected = nearest = dragged = particleSystem.nearest(p);
if (selected.node !== null){
// dragged.node.tempMass = 10000
dragged.node.fixed = true;
}
return false;
});
This is being used to control the default node "dragging" functionality. In here you can trigger the link you want.
I would add the link URL to the node json that you are passing back to define each node, so your original JSON posted becomes something like:
nodes:{
threadstarter:{'color':'red','shape':'dot','label':'Animals'},
reply1:{'color':'green','shape':'dot','label':'dog', link:'http://stackoverflow.com'},
reply2:{'color':'blue','shape':'dot','label':'cat', link:'http://stackoverflow.com'}
},
Then, you can update the mouseDown method to trigger the link (the current node in the mouse down method is "selected" so you can pull out the link like selected.node.data.link
If you look at the original source for the arbor site to see how they have done it, they have a clicked function that is called on mouseDown event and then essentially does:
$(that).trigger({type:"navigate", path:selected.node.data.link})
(edited for your purposes)
It is worth noting that whilst the Arbor framework and demos are open for use, their site isnt and is actually copyrighted, so only read that to see how they have done it, dont copy it ;)
With the above solutions (including the one implemented at www.arborjs.org) although nodes can open links when clicked, they also lose their ability to be dragged.
Based on a this question, that discusses how to distinguish between dragging and clicking events in JS, I wrote the following:
initMouseHandling:function(){
// no-nonsense drag and drop (thanks springy.js)
selected = null;
nearest = null;
var dragged = null;
var oldmass = 1
var mouse_is_down = false;
var mouse_is_moving = false
// set up a handler object that will initially listen for mousedowns then
// for moves and mouseups while dragging
var handler = {
mousemove:function(e){
if(!mouse_is_down){
var pos = $(canvas).offset();
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
nearest = particleSystem.nearest(_mouseP);
if (!nearest.node) return false
selected = (nearest.distance < 50) ? nearest : null
if(selected && selected.node.data.link){
dom.addClass('linkable')
} else {
dom.removeClass('linkable')
}
}
return false
},
clicked:function(e){
var pos = $(canvas).offset();
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
nearest = particleSystem.nearest(_mouseP);
if (!nearest.node) return false
selected = (nearest.distance < 50) ? nearest : null
if (nearest && selected && nearest.node===selected.node){
var link = selected.node.data.link
if (link.match(/^#/)){
$(that).trigger({type:"navigate", path:link.substr(1)})
}else{
window.open(link, "_blank")
}
return false
}
},
mousedown:function(e){
var pos = $(canvas).offset();
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
selected = nearest = dragged = particleSystem.nearest(_mouseP);
if (dragged.node !== null) dragged.node.fixed = true
mouse_is_down = true
mouse_is_moving = false
$(canvas).bind('mousemove', handler.dragged)
$(window).bind('mouseup', handler.dropped)
return false
},
dragged:function(e){
var old_nearest = nearest && nearest.node._id
var pos = $(canvas).offset();
var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
mouse_is_moving = true
if (!nearest) return
if (dragged !== null && dragged.node !== null){
var p = particleSystem.fromScreen(s)
dragged.node.p = p
}
return false
},
dropped:function(e){
if (dragged===null || dragged.node===undefined) return
if (dragged.node !== null) dragged.node.fixed = false
dragged.node.tempMass = 50
dragged = null
selected = null
$(canvas).unbind('mousemove', handler.dragged)
$(window).unbind('mouseup', handler.dropped)
_mouseP = null
if(mouse_is_moving){
// console.log("was_dragged")
} else {
handler.clicked(e)
}
mouse_is_down = false
return false
}
}
$(canvas).mousedown(handler.mousedown);
$(canvas).mousemove(handler.mousemove);
}
}
As you can see,
I made a difference between the clicked and mousedown handler functions.
I am opening links in new tabs. To simply redirect, change:
window.open(link, "_blank") for window.location = link.
The above must replace your previous initMouseHandling function in the renderer.js file.
Define "dom" as: var dom = $(canvas)
You have to add the following css code to give feedback to the user.
canvas.linkable{
cursor: pointer;
}
In the script renderer.js is the handler for the mouse events, so you can add your code to create your functions.
I modified the renderer.js to add the click and double-click functions.
var handler = {
clicked:function(e){
var pos = $(canvas).offset();
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
selected = nearest = dragged = particleSystem.nearest(_mouseP);
if (dragged.node !== null) dragged.node.fixed = true
$(canvas).bind('mousemove', handler.dragged)
$(window).bind('mouseup', handler.dropped)
$(canvas).bind('mouseup', handler.mypersonalfunction)
},
mypersonalfunction:function(e){
if (dragged===null || dragged.node===undefined) return
if (dragged.node !== null){
dragged.node.fixed = false
var id=dragged.node.name;
alert('Node selected: ' + id);
}
return false
},
You can check the clic and double-click functions in this page.
I add nodes and edges when a node has been clicked and I add edges to other nodes when the node has been double-clicked (the blue,yellow and green ones)
I´m looking for similar customization for selection over each node id value.
How would it be if instead of mouse-handler trigger, the selection was made via checkbox inside each node?
<input type=checkbox name=mycheckbox[]>
I'm nearly finished with this project but I have been beating my head against this problem for a day or so.
Big picture:
Im trying to create a link that will jump between tabs and find an anchor.
Details:
I need to create a link which triggers the function that hides the current div (using display: none)/shows another div (display: block;) and then goto an anchor on the page.
My first intuition was to do:
code:
<a onClick="return toggleTab(6,6);" href="#{anchor_tab_link_name}">{anchor_tab_link_name}</a>
Since the onClick should return true and then execute the anchor. However it loads but never goes to the anchor.
Here is the toggleTab function to give some context:
function toggleTab(num,numelems, anchor, opennum,animate) {
if ($('tabContent'+num).style.display == 'none'){
for (var i=1;i<=numelems;i++){
if ((opennum == null) || (opennum != i)){
var temph = 'tabHeader'+i;
var h = $(temph);
if (!h){
var h = $('tabHeaderActive');
h.id = temph;
}
var tempc = 'tabContent'+i;
var c = $(tempc);
if(c.style.display != 'none'){
if (animate || typeof animate == 'undefined')
Effect.toggle(tempc,'appear',{duration:0.4, queue:{scope:'menus', limit: 3}});
else
toggleDisp(tempc);
}
}
}
var h = $('tabHeader'+num);
if (h)
h.id = 'tabHeaderActive';
h.blur();
var c = $('tabContent'+num);
c.style.marginTop = '2px';
if (animate || typeof animate == 'undefined'){
Effect.toggle('tabContent'+num,'appear',{duration:0.4, queue:{scope:'menus', position:'end', limit: 3}});
}else{
toggleDisp('tabContent'+num);
}
}
}
So I posted this on a coding forum and a person told me that my tab code was done in prototype.
And that I should "Long story short: don't use onclick. Attach the data to the A tag and handle the click event yourself (using preventDefault() or similar) to do your tab-setting stuff, then when it's done, manually set your location to the hash tag."
I do understand what he is suggesting but I don't know how to implement it because I don't know much about javascript syntax.
If you can provide any hints or suggestions it would be amazing.
Update:
I tried to implement the solution below like this:
The link:
<a id="trap">trap</a>
Then adding the following js to the top of the page:
<script type="javascript">
document.getElementById("trap").click(function() { // bind click event to link
tabToggle(6,6);
var anchor = $(this).attr('href');
//setTimeout(infoSupport.gotoAnchor,600, anchor);
jumpToAnchor();
return false;
});
//Simple jump to anchor point
function jumpToAnchor(){
location.href = location.href+"#trap";
}
//Nice little jQuery scroll to id of any element
function scollToId(id){
window.scrollTo(0,$("#"+id).offset().top);
}
</script>
But unfortunately it simply doesn't seem to work for me. When I click the text simply nothing happens.
Anyone notice any apparent mistakes? I'm not used of working with javascript.
I found a lot simpler solution:
$(function(){
jumpToTarget('spot_to_go'); //This is what you put inside your function when the link is clicked.
function jumpToTarget(target){
var target_offset = $("#"+target).offset();
var target_top = target_offset.top;
$('html, body').animate({scrollTop:target_top}, 500);
}
});
Working demo:
http://jsbin.com/ivure/3/edit
So on the click event you do something like this:
//Untested
$('#trap').click(function(){
tabToggle(6,6);
var anchor = $(this).attr('href');
jumpToTarget(anchor);
return false;
});
Apparently a small delay was all I needed.
I used this for the link. This is preferred for my situation since I'm batch generating many of these links.
trap
Then I used this vanilla javascript
//Simple jump to anchor point
function jumpToAnchor(target){
setTimeout("window.location.hash=target",450);
}
This loads the link and instantly goes to the location. No jerkiness or anything.