moving SVG 'g' tag using JavaScript - javascript

I know SVG <g> tag is not having X and Y Attributes, and the only way to transfer it is using transform like transform="translate(x,y)" and transform="rotate(45 50 50)"
I'm trying to do the same programmable using JavaScript where I want to move a g tag having a combination of rect and text, below is my code, so what mistake I've so the g is not moving / translating once i click it?
var NS="http://www.w3.org/2000/svg";
var SVG=function(h,w){
var svg=document.createElementNS(NS,"svg");
svg.width=w;
svg.height=h;
return svg;
}
var svg=SVG(1200,1500);
document.body.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill,name) {
this.g= document.createElementNS(NS,"g");
this.name=name;
this.SVGObj= document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
this.text = document.createElementNS(NS, 'text');
this.text.setAttribute('x', x+10);
this.text.setAttribute('y', y+20);
this.text.setAttribute('fill', '#000');
this.text.textContent = '2';
this.g.appendChild(self);
this.g.appendChild(this.text)
this.g.addEventListener("click",this,false);
}
}
Object.defineProperty(myRect.prototype, "node", {
get: function node() {
return this.g; // this.SVGObj;
}
});
myRect.prototype.handleEvent= function(evt){
self = this.g;
switch (evt.type){
case "click":
// alert(this.name); // this.animate();
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true)
self.move = setInterval(()=>this.animate(),100);
else{
clearInterval(self.move);
self.parentNode.removeChild(self);
}
break;
default:
break;
}
}
myRect.prototype.animate = function() {
self = this.g;
self.transform="translate(200,200)";
// self.x.baseVal.value+=1;
// self.y.baseVal.value+=1;
};
var r= new myRect(50,50,30,30,'#'+Math.round(0xffffff * Math.random()).toString(16),'this is my name');
svg.appendChild(r.node);
UPDATE
I tried self.setAttribute('transform','translate(10,10)') but did not work, I was able to make ONE step ONE time only move using the self.setAttribute('transform','translate(10,10)'); where getItem(0) gets the first element in a transform attribute e.g. transform="translate(1, 1) scale(2)" where getItem(0) gets the translate(1, 1) matrix and getItem(1) gets the scale(2) as explained here
But this is still not what I need, I need continuous movement once I click the g till the loop is over.

<html>
<body>
<div class="svg"></div>
</body>
<script>
var NS="http://www.w3.org/2000/svg";
var SVG=function(h,w){
var svg=document.createElementNS(NS,"svg");
svg.width=w;
svg.height=h;
return svg;
}
var svg=SVG(1200,1500);
var div =document.querySelector(".svg");
div.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill,name) {
this.g= document.createElementNS(NS,"g");
this.name=name;
this.SVGObj= document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
this.text = document.createElementNS(NS, 'text');
this.text.setAttribute('x', x+10);
this.text.setAttribute('y', y+20);
this.text.setAttribute('fill', '#000');
this.text.textContent = '2';
this.g.appendChild(self);
this.g.appendChild(this.text)
this.g.addEventListener("click",this,false);
}
}
Object.defineProperty(myRect.prototype, "node", {
get: function node() {
return this.g; // this.SVGObj;
}
});
myRect.prototype.handleEvent= function(evt){
self = this.g;
switch (evt.type){
case "click":
// alert(this.name); // this.animate();
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true)
self.move = setInterval(()=>this.animate(evt),100);
else{
clearInterval(self.move);
self.parentNode.removeChild(self);
}
break;
default:
break;
}
}
myRect.prototype.animate = function(evt) {
self = this.g;
var recElement=self.childNodes[0];
recElement.setAttribute("x",10);
recElement.setAttribute("y",10);
//self.transform="translate(200,200)";
};
var r= new myRect(50,50,30,30,'#'+Math.round(0xffffff * Math.random()).toString(16),'this is my name');
svg.appendChild(r.node);
</script>

Thanks to Mike Williamson, I was able to solve it developing a customs transform function:
myRect.prototype.step = function(x,y) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y));
}
and call it by:
myRect.prototype.animate = function() {
self = this.g;
self.transform.baseVal.appendItem(this.step(1,1));
};
The full code, for any one interested, is:
var NS="http://www.w3.org/2000/svg";
var SVG=function(el){
return document.createElementNS(NS,el);
}
var svg = SVG("svg");
svg.width='100%';
svg.height='100%';
document.body.appendChild(svg);
class myRect {
constructor(x,y,h,w,fill,name) {
this.g= SVG("g");
this.name=name;
this.SVGObj= SVG('rect'); // document.createElementNS(NS,"rect");
self = this.SVGObj;
self.x.baseVal.value=x;
self.y.baseVal.value=y;
self.width.baseVal.value=w;
self.height.baseVal.value=h;
self.style.fill=fill;
self.onclick="click(evt)";
this.text = SVG('text');
this.text.setAttribute('x', x+10);
this.text.setAttribute('y', y+20);
this.text.setAttribute('fill', '#000');
this.text.textContent = name;
this.g.appendChild(self);
// this.g.appendChild(this.text); // if required to be loaded from start up
this.g.addEventListener("click",this,false);
//(e)=>alert(e.target.parentNode.parentNode); / this is to check what is clicked
}
}
Object.defineProperty(myRect.prototype, "node", {
get: ()=> return this.g;
});
myRect.prototype.handleEvent= function(evt){
self = evt.target.parentNode; // this returns the `g` element
switch (evt.type){
case "click":
if (typeof self.moving == 'undefined' || self.moving == false) self.moving = true;
else self.moving = false;
if(self.moving == true){
self.move = setInterval(()=>this.animate(),100);
self.appendChild(this.text); // show the text node
}
else{
clearInterval(self.move);
self.removeChild(self.childNodes[1]); // remove the text node
// self.parentNode.removeChild(self); // This removes the `g` element completly
}
break;
default:
break;
}
}
myRect.prototype.step = function(x,y) {
return svg.createSVGTransformFromMatrix(svg.createSVGMatrix().translate(x,y));
}
myRect.prototype.animate = function() {
self = this.g;
self.transform.baseVal.appendItem(this.step(1,1));
};
for (var i = 0; i < 10; i++) {
var x = Math.random() * 100,
y = Math.random() * 300;
var r= new myRect(x,y,10,10,'#'+Math.round(0xffffff * Math.random()).toString(16),'click to stop');
svg.appendChild(r.node);
}
it is also available at JSFiddle

Related

How to differentiate click+drag vs double click+drag

My project needs to differentiate single click, double click, click+drag, and double click+drag. I can already differentiate the first 3 events now, but no idea how to detect double click+drag. Any idea?
var holdStarter = null;
var holdDelay = 500;
var holdActive = false;
function onMouseDownEssence() {
holdStarter = setTimeout(function() {
holdStarter = null;
holdActive = true;
console.log("click and dragging ");
}, holdDelay);
}
function onMouseUpEssence(el) {
if (holdStarter) {
clearTimeout(holdStarter);
if (el.getAttribute("data-dblclick") == null) {
el.setAttribute("data-dblclick", 1);
setTimeout(
function() {
if (el.getAttribute("data-dblclick") == 1) {
console.log("single clicked ");
}
el.removeAttribute("data-dblclick");
}, 300);
} else {
el.removeAttribute("data-dblclick");
console.log("double clicked ");
}
} else if (holdActive) {
console.log("click and drag done");
holdActive = false;
}
}
I removed one timeout and added a variable dragTarget to keep track of the dragged element.
var holdStarter = null;
var dblDelay = 300;
var holdDelay = 500;
var holdActive = false;
var dragTarget = null;
var dbl = "data-dblclick";
window.addEventListener('mousedown',function(e){
dragTarget = e.target;
holdStarter = new Date().valueOf();
});
window.addEventListener('mouseup',function(e){
var el = e.target;
var holdActive = (new Date().valueOf() - holdStarter) > holdDelay;
if (holdActive) {
if (el.getAttribute(dbl) == null) {
console.log("drag done");
} else {
console.log("double drag done");
el.removeAttribute(dbl);
}
holdActive = false;
} else if (el.getAttribute(dbl) == null) {
el.setAttribute(dbl, 1);
setTimeout(function() {
if (el.getAttribute(dbl) == 1 && !dragTarget) {
console.log("single clicked ");
el.removeAttribute(dbl);
}
}, dblDelay);
} else {
console.log("double clicked");
el.removeAttribute(dbl);
}
dragTarget = null;
});

if statement getElementById.src

SOLVED
var player = 0;
if(player == 0)
{
document.getElementById("sp").src = "../image/start.png";
player = 1;
}
else if(player == 1)
{
document.getElementById("sp").src = "../image/stop.png";
player = 0;
}
I'm trying to make play/pause button in JavaScript.
The first text only version works fine using innerHtml but I need to use an image file for the final version.
I got 3 folders in my root dir:
image (where the image files are)
slide (where the php file are)
javascript (where the js file is placed)
In my php file:
<img src="../image/stop.png" id="sp">
<script type="text/javascript" src="../javascript/page1.js"></script>
In my js file:
if(document.getElementById("sp").src == "../image/stop.png") {
document.getElementById("sp").src = "../image/start.png";
} else if(document.getElementById("sp").src == "../image/start.png") {
document.getElementById("sp").src = "../image/stop.png";
}
I have search for a sulotion to this but I can't get it to work.
The old code for the text verison looks like this.
if(document.getElementById("sp").innerHTML == "Stop") {
document.getElementById("sp").innerHTML = "Start";
} else if(document.getElementById("sp").innerHTML == "Start") {
document.getElementById("sp").innerHTML = "Stop";
}
The code is in a function triggerd on an click event
Can someone please help me get this to work?
Here is the whloe js code.
I know this may not be optmal written but I'm very new to js
var CB =
{
addEvent : function(element, event, action)
{
if (element.addEventListener){
element.addEventListener(event, action, false);
}
else
{
element.attachEvent("on" + event, action);
}
}
}
Timer = function(callback, delay)
{
var timerId, start, remaining = delay;
this.pause = function()
{
window.clearTimeout(timerId);
remaining -= new Date() - start;
};
this.resume = function()
{
start = new Date();
timerId = window.setTimeout(callback, remaining);
};
this.resume();
};
var synlig = 0.0;
function visa(pic)
{
synlig += 0.1;
if(synlig < 1.0)
{
var x = String(synlig)
pic.style.opacity = x;
}
else
{
pic.style.opacity="1.0";
return;
}
setTimeout(function(){visa(pic)}, 120);
}
var ejSynlig = 1.0
function visaEJ(pic)
{
ejSynlig -= 0.1;
if(ejSynlig > 0.0)
{
var x = String(ejSynlig)
pic.style.opacity = x;
}
else
{
pic.style.opacity="0.0";
return;
}
setTimeout(function(){visaEJ(pic)}, 120);
}
var synlig2 = 0.0;
function visa2(pic2)
{
synlig2 += 0.1;
if(synlig2 < 1.0)
{
var x = String(synlig2)
pic2.style.opacity = x;
}
else
{
pic2.style.opacity="1.0";
return;
}
setTimeout(function(){visa2(pic2)}, 120);
}
var ejSynlig2 = 1.0
function visaEJ2(pic2)
{
ejSynlig2 -= 0.1;
if(ejSynlig2 > 0.0)
{
var x = String(ejSynlig2)
pic2.style.opacity = x;
}
else
{
pic2.style.opacity="0.0";
return;
}
setTimeout(function(){visaEJ2(pic2)}, 120);
}
var pic = document.getElementById("bild");
var t1s = new Timer(function(){visa(pic)}, 5000);
var t1h = new Timer(function(){visaEJ(pic)}, 11000);
var pic2 = document.getElementById("bild2");
var t2s = new Timer(function(){visa2(pic2)}, 11500);
var t2h = new Timer(function(){visaEJ2(pic2)}, 15000);
function imgs()
{
var p = document.getElementById("sp");
var x1, y1, x2, y2;
if(document.getElementById("sp").src == "../image/stop.png")
{
x1 = t1s.pause();
y1 = t1h.pause();
x2 = t2s.pause();
y2 = t2h.pause();
}
else if(document.getElementById("sp").src == "../image/start.png")
{
x1 = t1s.resume();
y1 = t1h.resume();
x2 = t2s.resume();
y2 = t2h.resume();
}
CB.addEvent(p, "click", x1);
CB.addEvent(p, "click", y1);
CB.addEvent(p, "click", x2);
CB.addEvent(p, "click", y2);
}
function snd()
{
var sndP = document.getElementById("sndP");
function playS()
{
sndP.volume = 0.5;
sndP.play();
}
function pauseS()
{
sndP.pause();
}
var p = document.getElementById("sp");
var y;
if(document.getElementById("sp").src == "../image/stop.png")
{
y = pauseS();
}
else if(document.getElementById("sp").src == "../image/start.png")
{
y = playS();
}
CB.addEvent(p, "click", y);
}
function theshit()
{
imgs();
snd();
if(document.getElementById("sp").src == "../image/stop.png")
{
document.getElementById("sp").src = "../image/start.png";
}
else if(document.getElementById("sp").src == "../image/start.png")
{
document.getElementById("sp").src = "../image/stop.png";
}
}
var sp = document.getElementById("sp");
CB.addEvent(sp, "click", theshit);
sndP.volume = 0.5;
sndP.play();
if(document.getElementById("sp").src == "../image/stop.png")
{
document.getElementById("sp").setAttribute("src","../image/start.png");
}
else if(document.getElementById("sp").src == "../image/start.png")
{
document.getElementById("sp").setAttribute("src","../image/stop.png");
}
So you want to toggle the image when clicked?
if you set something to src and then get it, it might look a little bit different.
a better solution, than to check if something has a specific url, you could save the isPlaying variable manually. this way you would writeonly to the DOM, which is faster, then reading the src value manually all the time.
var isPlaying = false;
var stopImg = "../images/stop.png";
var playImg = "../images/start.png";
var el = document.getElementById("sp");
$("button").click(function() {
// toggle
el.src = isPlaying ? stopImg : playImg;
// also toggle the var
isPlaying = !isPlaying;
});
even a better way is to do it using CSS and for example a background-image...
button {
/*default state*/
background-image : url('/images/play.png');
}
button.isPlaying {
/*state when playing*/
background-image : url('/images/stop.png');
}
and then in JS
$("button").click(function() {
$(this).toggleClass("isPlaying");
});

HammerJS pull to refresh

I know very little about Javascript but need to use hammer.js in a project I'm working on.
Instead of replacing/refreshing the image im trying to use the pull to refresh scrit to refresh the page.
Ive tried adding
location.reload();
to the script but to no avail, can anyone shed any light on this.
http://eightmedia.github.io/hammer.js/
This is the code im using
/**
* requestAnimationFrame and cancel polyfill
*/
(function() {
var lastTime = 0;
var vendors = ['ms', 'moz', 'webkit', 'o'];
for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
window.cancelAnimationFrame =
window[vendors[x]+'CancelAnimationFrame'] || window[vendors[x]+'CancelRequestAnimationFrame'];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function(callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function() { callback(currTime + timeToCall); },
timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function(id) {
clearTimeout(id);
};
}());
/**
* pull to refresh
* #type {*}
*/
var PullToRefresh = (function() {
function Main(container, slidebox, slidebox_icon, handler) {
var self = this;
this.breakpoint = 80;
this.container = container;
this.slidebox = slidebox;
this.slidebox_icon = slidebox_icon;
this.handler = handler;
this._slidedown_height = 0;
this._anim = null;
this._dragged_down = false;
this.hammertime = Hammer(this.container)
.on("touch dragdown release", function(ev) {
self.handleHammer(ev);
});
};
/**
* Handle HammerJS callback
* #param ev
*/
Main.prototype.handleHammer = function(ev) {
var self = this;
switch(ev.type) {
// reset element on start
case 'touch':
this.hide();
break;
// on release we check how far we dragged
case 'release':
if(!this._dragged_down) {
return;
}
// cancel animation
cancelAnimationFrame(this._anim);
// over the breakpoint, trigger the callback
if(ev.gesture.deltaY >= this.breakpoint) {
container_el.className = 'pullrefresh-loading';
pullrefresh_icon_el.className = 'icon loading';
this.setHeight(60);
this.handler.call(this);
}
// just hide it
else {
pullrefresh_el.className = 'slideup';
container_el.className = 'pullrefresh-slideup';
this.hide();
}
break;
// when we dragdown
case 'dragdown':
this._dragged_down = true;
// if we are not at the top move down
var scrollY = window.scrollY;
if(scrollY > 5) {
return;
} else if(scrollY !== 0) {
window.scrollTo(0,0);
}
// no requestAnimationFrame instance is running, start one
if(!this._anim) {
this.updateHeight();
}
// stop browser scrolling
ev.gesture.preventDefault();
// update slidedown height
// it will be updated when requestAnimationFrame is called
this._slidedown_height = ev.gesture.deltaY * 0.4;
break;
}
};
/**
* when we set the height, we just change the container y
* #param {Number} height
*/
Main.prototype.setHeight = function(height) {
if(Modernizr.csstransforms3d) {
this.container.style.transform = 'translate3d(0,'+height+'px,0) ';
this.container.style.oTransform = 'translate3d(0,'+height+'px,0)';
this.container.style.msTransform = 'translate3d(0,'+height+'px,0)';
this.container.style.mozTransform = 'translate3d(0,'+height+'px,0)';
this.container.style.webkitTransform = 'translate3d(0,'+height+'px,0) scale3d(1,1,1)';
}
else if(Modernizr.csstransforms) {
this.container.style.transform = 'translate(0,'+height+'px) ';
this.container.style.oTransform = 'translate(0,'+height+'px)';
this.container.style.msTransform = 'translate(0,'+height+'px)';
this.container.style.mozTransform = 'translate(0,'+height+'px)';
this.container.style.webkitTransform = 'translate(0,'+height+'px)';
}
else {
this.container.style.top = height+"px";
}
};
/**
* hide the pullrefresh message and reset the vars
*/
Main.prototype.hide = function() {
container_el.className = '';
this._slidedown_height = 0;
this.setHeight(0);
cancelAnimationFrame(this._anim);
this._anim = null;
this._dragged_down = false;
};
/**
* hide the pullrefresh message and reset the vars
*/
Main.prototype.slideUp = function() {
var self = this;
cancelAnimationFrame(this._anim);
pullrefresh_el.className = 'slideup';
container_el.className = 'pullrefresh-slideup';
this.setHeight(0);
setTimeout(function() {
self.hide();
}, 500);
};
/**
* update the height of the slidedown message
*/
Main.prototype.updateHeight = function() {
var self = this;
this.setHeight(this._slidedown_height);
if(this._slidedown_height >= this.breakpoint){
this.slidebox.className = 'breakpoint';
this.slidebox_icon.className = 'icon arrow arrow-up';
}
else {
this.slidebox.className = '';
this.slidebox_icon.className = 'icon arrow';
}
this._anim = requestAnimationFrame(function() {
self.updateHeight();
});
};
return Main;
})();
function getEl(id) {
return document.getElementById(id);
}
var container_el = getEl('container');
var pullrefresh_el = getEl('pullrefresh');
var pullrefresh_icon_el = getEl('pullrefresh-icon');
var image_el = getEl('random-image');
var refresh = new PullToRefresh(container_el, pullrefresh_el, pullrefresh_icon_el);
// update image onrefresh
refresh.handler = function() {
var self = this;
// a small timeout to demo the loading state
setTimeout(function() {
var preload = new Image();
preload.onload = function() {
image_el.src = this.src;
self.slideUp();
};
preload.src = 'http://lorempixel.com/800/600/?'+ (new Date().getTime());
}, 1000);
};
There is a Beer involved for anyone that can fix this :)
http://jsfiddle.net/zegermens/PDcr9/1/
You're assigning the return value of the location.reload function to the src attribute of an Image element. I'm not sure if the function even has a return value, https://developer.mozilla.org/en-US/docs/Web/API/Location.reload
Try this:
// update image onrefresh
refresh.handler = function() {
var self = this;
// a small timeout to demo the loading state
setTimeout(function() {
window.location.reload();
}, 1000);
};

Up/Down/Left/Right keyboard navigation with jQuery?

I have a list of div's all with a set and equal height/width that are float:left so they sit next to each other and fold under if that parent is smaller than the combined with of the items.
Pretty standard.
This is to create a list of the twitter bootstrap icons, it gives something like this:
I have added next/previous keyboard navigation using the code below, however you will notice that the up/down arrow keys are mapped to call the left/right functions. What I have no idea how to do is to actually do the up/down navigation?
JsFiddle
(function ($) {
$.widget("ui.iconSelect", {
// default options
options: {
},
$select: null,
$wrapper: null,
$list: null,
$filter: null,
$active: null,
icons: {},
keys: {
left: 37,
up: 38,
right: 39,
down: 40
},
//initialization function
_create: function () {
var that = this;
that.$select = that.element;
that.$wrapper = $('<div class="select-icon" tabindex="0"></div>');
that.$filter = $('<input class="span12" tabindex="-1" placeholder="Filter by class name..."/>').appendTo(that.$wrapper);
that.$list = $('<div class="select-icon-list"></div>').appendTo(that.$wrapper);
//build the list of icons
that.element.find('option').each(function () {
var $option = $(this);
var icon = $option.val();
that.icons[icon] = $('<a data-class="' + icon + '"><i class="icon ' + icon + '"></i></a>');
if ($option.is(':selected')) {
that.icons[icon].addClass('selected active');
}
that.$list.append(that.icons[icon]);
});
that.$wrapper.insertBefore(that.$select);
that.$select.addClass('hide');
that._setupArrowKeysHandler();
that._setupClickHandler();
that._setupFilter();
that.focus('selected');
},
focus: function (type) {
var that = this;
if (that.$active === null || that.$active.length == 0) {
if (type == 'first') {
that.$active = that.$list.find('a:visible:first');
} else if (type == 'last') {
that.$active = that.$list.find('a:visible:last');
} else if (type == 'selected') {
that.$active = that.$list.find('a.selected:visible:first');
that.focus('first');
}
}
that.$active.addClass('active');
var toScroll = ((that.$list.scrollTop() + that.$active.position().top)-that.$list.height()/2)+that.$active.height()/2;
//that.$list.scrollTop((that.$list.scrollTop() + top)-that.$list.height()/2);
that.$list.stop(true).animate({
scrollTop: toScroll,
queue: false,
easing: 'linear'
}, 200);
if (type === 'selected') {
return false;
}
that.$select.val(that.$active.data('class'));
that.$select.trigger('change');
},
_setupArrowKeysHandler: function () {
var that = this;
that.$wrapper.on('keydown', function (e) {
switch (e.which) {
case that.keys.left:
that.moveLeft();
break;
case that.keys.up:
that.moveUp();
break;
case that.keys.right:
that.moveRight();
break;
case that.keys.down:
that.moveDown();
break;
case 16:
return true;
case 9:
return true;
break;
default:
that.$filter.focus();
return true;
}
return false;
});
},
_setupFilter: function(){
var that = this;
that.$filter.on('keydown keyup keypress paste cut change', function(e){
that.filter(that.$filter.val());
});
},
_setupClickHandler: function () {
var that = this;
that.$list.on('click', 'a', function () {
that.$wrapper.focus();
that.$active.removeClass('active');
that.$active = $(this);
that.focus('first');
});
},
moveUp: function () {
var that = this;
return that.moveLeft();
},
moveDown: function () {
var that = this;
return that.moveRight();
},
moveLeft: function () {
var that = this;
that.$active.removeClass('active');
that.$active = that.$active.prevAll(':visible:first');
that.focus('last');
return false;
},
moveRight: function () {
var that = this;
that.$active.removeClass('active');
that.$active = that.$active.nextAll(':visible:first');
that.focus('first');
return false;
},
filter: function(word){
var that = this;
var regexp = new RegExp(word.toLowerCase());
var found = false;
$.each(that.icons, function(i, $v){
found = regexp.test(i);
if(found && !$v.is(':visible')){
$v.show();
} else if(!found && $v.is(':visible')){
$v.hide();
}
});
}
});
})(jQuery);
Perhaps something like this: http://jsfiddle.net/QFzCY/
var blocksPerRow = 4;
$("body").on("keydown", function(e){
var thisIndex = $(".selected").index();
var newIndex = null;
if(e.keyCode === 38) {
// up
newIndex = thisIndex - blocksPerRow;
}
else if(e.keyCode === 40) {
// down
newIndex = thisIndex + blocksPerRow;
}
if(newIndex !== null) {
$(".test").eq(newIndex).addClass("selected").siblings().removeClass("selected");
}
});
Basically, you set how many items there are in a row and then find the current index and subtract or add that amount to select the next element via the new index.
If you need to know how many blocks per row there are, you could do this:
var offset = null;
var blocksPerRow = 0;
$(".test").each(function(){
if(offset === null) {
offset = $(this).offset().top;
}
else if($(this).offset().top !== offset) {
return false;
}
blocksPerRow++;
});
To deal with your 'edge' cases, you could do:
if(newIndex >= $(".test").length) {
newIndex = $(".test").length - newIndex;
}
moveUp: function () {
var that = this;
var index = $(this).index();
var containerWidth = parseInt( $('.select-icon-list').innerWidth(), 10);
var iconWidth = parseInt( $('.select-icon-list > a').width(), 10);
var noOfCols = Math.floor( containerWidth / iconWidth );
var newIndex = ( (index - noOfCols) < 0 ) ? index : (index - noOfCols);
var elem = $('.select-icon-list > a')[index];
},
Cache what ever remains static.

I move this code into a separate function and it stops working, why?

Here is an excerpt of my code. You can ignore most of it: the bit of concern is with the refreshDimensions method, the call to said method inside zoomTo, and the block of code after that call.
function refreshDimensions(node) {
_("refreshdimensions");
t = $("#contents");
var other = $(selectednode).parent().parent(":not(#contents)");
if(!other.length) {
other = selectednode || zoomednode || start;
t.width("100%");
} else {
t.width("100%");
t.width((t.width() + other.position().left));
}
t.height($(other).position().top);
/* Begin animating */
t.animate({ fontSize: zoom }, {duration: 0, queue: false });
//
}
function zoomTo(node, select) {
var oldzoom, zoomdepth, t;
oldzoom = zoomednode;
if($(node)[0] != $(zoomednode)[0]) {
savedepth = t = zoomdepth = $(node).parents("ul").length;
if(!zoomednode)
zoomednode = topChapter;
$(zoomednode).toggleClass("zoomednode", false);
if(!node)
node = topChapter;
/* capture values */
var sz;
var capp = cBaseSz.slice((zoom = zoomnum = sz = parseFloat(cBaseSz)+"").length);
/* end capture */
while(--t > 0) {
zoomnum = (zoom *= 1.15);
}
zoom += capp;
zoomdepth -= $(zoomednode).parents("ul").length;
if(zoomdepth < 0)
zoomdepth *= -1;
zoomednode = node;
zoomednode.toggleClass("zoomednode", true);
switch(select) {
case 0:
case false:
default:
break;
case true:
case 1:
toggleNode(selectednode, false);
toggleNode(node, true);
break;
case 2:
toggleNode(zoomednode, false);
zoomednode = 0;
}
/* Handle showing/hiding */
//////////////////////////////
//
var showzoom = 1, showselect = 1, showidea = 1, seldepth, zdepth, showlist, hidelist = {};
/* This is the 'brute force' way of doing it, horribly inefficient */
if(zoomednode)
zdepth = $(zoomednode).parents(".chapter").length;
if(selectednode)
seldepth = $(selectednode).parents(".chapter").length;
else
seldepth = zdepth;
if(!seldepth)
seldepth = zdepth = 0;
showlist=$(".chapter, .idea").filter( function() {
if($(this).parents("li").length < (zdepth+showzoom))
return true;
else {
hidelist = $(hidelist).add(this);
return false;
}
});
$(showlist).show()/*.not(hidelist)*/;
if(hidelist && hidelist.length) {
$(hidelist).hide();
}
/* End showing/hiding */
refreshDimensions(node);
if(node) {
_("top: " + $(zoomednode).position().top);
$("html,body").stop().animate(
{ scrollTop: $(zoomednode).position().top - topAdjust }, {duration: 60+60*zoomdepth, queue: false }, 0);
}
}
else {
var dest;
if($(zoomednode).parents(".chapter").length > 1)
dest = $(zoomednode).parent().parent().prevAll(".chapter:first .chapterheading:first");
else {
// if($(zoomednode)[0] != $("#contents")[0]) {
toggleNode(selectednode, false);
dest = $("#contents");
// } else
// dest = $(start);
}
zoomTo(dest, 0);
}
}
So my problem is that when I move the bit of code after the call to refreshDimensions (the block beginning with 'if(node) {'), into refreshDimensions (at the end), the desired effect stops working. I have dumped all the variables that the line uses into the console and they are consistent across both instances, yet when I move the code to refreshDimensions my page gets 'trapped' at the top of the screen and won't scroll at all. This really has me stumped as everything points to that it should work exactly the same...
turns out it was because 'zoomdepth' should have been a global rather than local variable.

Categories