Creating a popup menu with bookmarklets - javascript
This is my first question, so please give me any pointers on how I could ask better ones.
Anyway, how would I make a togglable menu that displays links, that activates using a bookmarklet. I have tried to find answers, but all were fruitless. Would I need to create a new element for this?
You will need to create the pop-up menu using vanilla JS. I also implemented drag functionality. The only thing this needs is to correctly set the position when a page is scrolled.
DOM layout
The most important elements and styles below are required.
<div style="position:absolute; z-index:2147483647">
<div style="position: relative">
<div style="position:relative; display:inline-block; left:0">Bookmarklet Links</div>
<div style="position:relative; float:right">×</div>
</div>
<div>
<p>Click the links to open a new tab!</p>
<ul>
<li>
Google
</li>
<li>
Bing
</li>
<li>
DuckDuckGO
</li>
</ul>
</div>
</div>
You can save the following bookmarklet:
javascript:!function(){var c=0x1f4,d=0x12c,e='#AAA',f=0x1,g=0x20,h='#444',i='#FFF',j='Bookmarklet\x20Links',k=~~(document['documentElement']['clientWidth']/0x2-c/0x2),l=~~(document['documentElement']['clientHeight']/0x2-d/0x2),m=~~(0.8*g),n=document['createElement']('DIV');Object['assign'](n['style'],{'position':'absolute','left':k+'px','top':l+'px','zIndex':Number['MAX_SAFE_INTEGER'],'width':c+'px','height':d+'px','background':e,'border':f+'px\x20solid\x20black'});var o=document['createElement']('DIV');Object['assign'](o['style'],{'position':'relative','width':c+'px','height':g+'px','background':h,'borderBottom':f+'px\x20solid\x20black'});var p=document['createElement']('DIV');Object['assign'](p['style'],{'position':'relative','display':'inline-block','left':0x0,'width':~~(c-0x2*m)+'px','lineHeight':g+'px','color':i,'fontSize':~~(0.667*g)+'px','marginLeft':~~(m/0x3)+'px'}),p['textContent']=j;var q=document['createElement']('DIV'),r=~~((g-m)/0x2);Object['assign'](q['style'],{'position':'relative','float':'right','right':r+'px','top':r+'px','width':m+'px','height':m+'px','background':'#F00','border':f+'px\x20solid\x20black','color':'#FFF','lineHeight':m+'px','textAlign':'center','fontSize':m+'px','marginLeft':'auto','marginRight':0x0});var s=document['createElement']('DIV');Object['assign'](s['style'],{'padding':'1em'});var t=document['createElement']('P');t['textContent']='Click\x20the\x20links\x20to\x20open\x20a\x20new\x20tab!',s['appendChild'](t);var u=document['createElement']('UL');[{'name':'Google','url':'https://www.google.com'},{'name':'Bing','url':'https://www.bing.com'},{'name':'DuckDuckGO','url':'https://duckduckgo.com'}]['forEach'](c=>{var d=document['createElement']('LI'),e=document['createElement']('A');e['setAttribute']('href',c['url']),e['setAttribute']('target','_blank'),e['textContent']=c['name'],d['appendChild'](e),u['appendChild'](d);}),s['appendChild'](u),q['addEventListener']('click',function c(d){q['removeEventListener']('click',c,!0x1);o['removeChild'](q);n['removeChild'](o);n['removeChild'](s);document['body']['removeChild'](n);},!0x1),q['textContent']='×',o['appendChild'](p),o['appendChild'](q),n['appendChild'](o),n['appendChild'](s),document['body']['appendChild'](n),function(c){var d=function(c){var d=c['getBoundingClientRect'](),e=window['pageXOffset']||document['documentElement']['scrollLeft'],f=window['pageYOffset']||document['documentElement']['scrollTop'];return{'top':d['top']+f,'left':d['left']+e};}(c['parentElement']),e=!0x1,f={'x':0x0,'y':0x0},g={'x':d['left'],'y':d['top']};c['parentElement']['addEventListener']('mousedown',function(d){e=!0x0,f['x']=d['clientX'],f['y']=d['clientY'],c['parentElement']['style']['cursor']='move';}),c['parentElement']['addEventListener']('mouseup',function(d){e=!0x1,g['x']=parseInt(c['parentElement']['style']['left'])||0x0,g['y']=parseInt(c['parentElement']['style']['top'])||0x0,c['parentElement']['style']['cursor']='auto';}),document['addEventListener']('mousemove',function(d){if(!e)return;var h={'x':d['clientX']-f['x'],'y':d['clientY']-f['y']},i={'x':g['x']+h['x'],'y':g['y']+h['y']};i['x']<0x0?i['x']=0x0:i['x']+c['parentElement']['offsetWidth']>document['documentElement']['clientWidth']&&(i['x']=document['documentElement']['clientWidth']-c['parentElement']['offsetWidth']);i['y']<0x0?i['y']=0x0:i['y']+c['parentElement']['offsetHeight']>document['documentElement']['clientHeight']&&(i['y']=document['documentElement']['clientHeight']-c['parentElement']['offsetHeight']);c['parentElement']['style']['left']=i['x']+'px',c['parentElement']['style']['top']=i['y']+'px';});}(o);}(window);
Method of minification and obfuscation
I minified the code below using: https://javascript-minifier.com/
I obfuscated the resulting minified code using: https://obfuscator.io/
For the obfuscator, I set "Identifier Names Generator" to "mangled" and checked "Rename Globals".
Important: Unselect "String Array" or you cannot add new link entries.
Caveats
The script doesn't allow moving beyond the initial width and height. The following width and height methods from this post could be incorporated to fix this limitation.
Source code
(function(window) {
var links = [{
name: 'Google',
url: 'https://www.google.com'
}, {
name: 'Bing',
url: 'https://www.bing.com'
}, {
name: 'DuckDuckGO',
url: 'https://duckduckgo.com'
}];
var props = {
width: 500,
height: 300,
background: '#AAA',
borderThickness: 1,
headerHeight: 32,
headerBackground: '#444',
headerTitleColor: '#FFF',
windowTitle: 'Bookmarklet Links'
};
var windowPosition = {
left: ~~((document.documentElement.clientWidth / 2) - (props.width / 2)),
top: ~~((document.documentElement.clientHeight / 2) - (props.height / 2)),
}
var btnSize = ~~(props.headerHeight * 0.8);
var popupEl = document.createElement('DIV');
Object.assign(popupEl.style, {
position: 'absolute',
left: windowPosition.left + 'px',
top: windowPosition.top + 'px',
zIndex: Number.MAX_SAFE_INTEGER,
width: props.width + 'px',
height: props.height + 'px',
background: props.background,
border: props.borderThickness + 'px solid black'
});
var popupHeader = document.createElement('DIV');
Object.assign(popupHeader.style, {
position: 'relative',
width: (props.width) + 'px',
height: props.headerHeight + 'px',
background: props.headerBackground,
borderBottom: props.borderThickness + 'px solid black'
});
var popupHeaderTitle = document.createElement('DIV');
Object.assign(popupHeaderTitle.style, {
position: 'relative',
display: 'inline-block',
left: 0,
width: ~~(props.width - btnSize * 2) + 'px',
lineHeight: props.headerHeight + 'px',
color: props.headerTitleColor,
fontSize: ~~(props.headerHeight * 0.667) + 'px',
marginLeft: ~~(btnSize / 3) + 'px'
});
popupHeaderTitle.textContent = props.windowTitle;
var closeButton = document.createElement('DIV');
var margin = ~~((props.headerHeight - btnSize) / 2);
Object.assign(closeButton.style, {
position: 'relative',
float: 'right',
right: margin + 'px',
top: margin + 'px',
width: btnSize + 'px',
height: btnSize + 'px',
background: '#F00',
border: props.borderThickness + 'px solid black',
color: '#FFF',
lineHeight: btnSize + 'px',
textAlign: 'center',
fontSize: btnSize + 'px',
marginLeft: 'auto',
marginRight: 0
});
var popupBody = document.createElement('DIV');
Object.assign(popupBody.style, {
padding: '1em'
});
var p = document.createElement('P');
p.textContent = 'Click the links to open a new tab!';
popupBody.appendChild(p);
var listEl = document.createElement('UL');
links.forEach(link => {
var itemEl = document.createElement('LI');
var anchorEl = document.createElement('A');
anchorEl.setAttribute('href', link.url);
anchorEl.setAttribute('target', '_blank');
anchorEl.textContent = link.name;
itemEl.appendChild(anchorEl);
listEl.appendChild(itemEl);
});
popupBody.appendChild(listEl);
closeButton.addEventListener('click', destroyWindow, false);
closeButton.textContent = '×';
popupHeader.appendChild(popupHeaderTitle);
popupHeader.appendChild(closeButton);
popupEl.appendChild(popupHeader);
popupEl.appendChild(popupBody);
document.body.appendChild(popupEl);
draggable(popupHeader);
function destroyWindow(e) {
closeButton.removeEventListener('click', destroyWindow, false);
popupHeader.removeChild(closeButton);
popupEl.removeChild(popupHeader);
popupEl.removeChild(popupBody);
document.body.removeChild(popupEl);
}
/* Source: https://plainjs.com/javascript/styles/get-the-position-of-an-element-relative-to-the-document-24/ */
function offset(el) {
var rect = el.getBoundingClientRect(),
scrollLeft = window.pageXOffset || document.documentElement.scrollLeft,
scrollTop = window.pageYOffset || document.documentElement.scrollTop;
return { top: rect.top + scrollTop, left: rect.left + scrollLeft }
}
/* Source: https://gist.github.com/remarkablemark/5002d27442600510d454a5aeba370579 */
function draggable(el) {
var initialOffset = offset(el.parentElement);
var isMouseDown = false;
var currPos = { x : 0, y : 0 };
var elPos = { x : initialOffset.left, y : initialOffset.top };
el.parentElement.addEventListener('mousedown', onMouseDown);
function onMouseDown(event) {
isMouseDown = true;
currPos.x = event.clientX;
currPos.y = event.clientY;
el.parentElement.style.cursor = 'move';
}
el.parentElement.addEventListener('mouseup', onMouseUp);
function onMouseUp(event) {
isMouseDown = false;
elPos.x = parseInt(el.parentElement.style.left) || 0;
elPos.y = parseInt(el.parentElement.style.top) || 0;
el.parentElement.style.cursor = 'auto';
}
document.addEventListener('mousemove', onMouseMove);
function onMouseMove(event) {
if (!isMouseDown) return;
var delta = { x : event.clientX - currPos.x, y: event.clientY - currPos.y };
var pos = { x : elPos.x + delta.x, y : elPos.y + delta.y };
if (pos.x < 0) {
pos.x = 0;
} else if (pos.x + el.parentElement.offsetWidth > document.documentElement.clientWidth) {
pos.x = document.documentElement.clientWidth - el.parentElement.offsetWidth;
}
if (pos.y < 0) {
pos.y = 0;
} else if (pos.y + el.parentElement.offsetHeight > document.documentElement.clientHeight) {
pos.y = document.documentElement.clientHeight - el.parentElement.offsetHeight;
}
el.parentElement.style.left = pos.x + 'px';
el.parentElement.style.top = pos.y + 'px';
}
}
})(window);
Improving
You will notice that if your cursor goes off-screen while dragging (and you release the button) the window will be stuck in drag. You could detect this globally, but you will also need to figure out how re reinitialize the position to last known "good" position.
document.addEventListener('mouseup', onGlobalMouseUp);
function onGlobalMouseUp(event) {
if (
(event.clientX < 0 || event.clientX > document.documentElement.clientWidth) ||
(event.clientY < 0 || event.clientY > document.documentElement.clientHeight)
) {
if (isMouseDown) {
isMouseDown = false; // Draggged off-screen
popupEl.style.cursor = 'auto';
}
}
}
Lastly, don't spam the bookmarklet button, because it will create multiple instances of the same window. Code can be added to detect the presence of the window before creating a new one. Closing it could hide it, so it will just make the existing one visible again. Multiple windows will break the close listener.
Related
javascript global variable good practice
I have a few variables pertaining to the height of elements an want to update them using $(window).on('resize'[...]), as well as calculate them when first loading the page so naturally I would rather make a function for doing that, instead of repeating the code. Is it bad practice to make these variables global so that I can update them with a function, or is there any other way to do this? var hSum = 0 for (var i = 0; i < content.length; i++) { var contentH = $(content[i]).height() hSum += contentH } var windowHeight = $(window).height(), footerHeight = footer.height(), heightDocument = windowHeight + hSum + footer.height() - 20; This is the entirety of the script function scrollFooter(scrollY, footerHeight) { if (scrollY >= footerHeight) { $('footer').css({ 'bottom': '0px' }); } else { $('footer').css({ 'bottom': '-' + footerHeight + 'px' }); } } $(document).ready(function() { var content = $('.content'), header = $('header'), footer = $('footer'), headerContainer = header.find('.container'), headerBackground = header.find('.background'), nav = $('.navbar') var hSum = 0 for (var i = 0; i < content.length; i++) { var contentH = $(content[i]).height() hSum += contentH } var windowHeight = $(window).height(), footerHeight = footer.height(), documentHeight = windowHeight + hSum + footer.height() - 20; $('#scroll-animate, #scroll-animate-main').css({ 'height': documentHeight + 'px' }) $('header').css({ 'height': windowHeight + 'px' }) $('.wrapper-parallax').css({ 'margin-top': windowHeight + 'px' }) scrollFooter(window.scrollY, footerHeight); setTimeout(function fadeHeaderIn() { headerContainer.css('opacity', '1') headerBackground.css('transform', 'scale(1.25, 1.25)') }, 300) $(window).on('scroll', function() { scroll = window.scrollY $('#scroll-animate-main').css({ 'top': '-' + scroll + 'px' }); scrollFooter(scroll, footerHeight); nav.toggleClass('hidden', scroll < windowHeight) }) nav.on("mouseenter", function() { nav.removeClass('minimized') }) nav.on("mouseleave", function() { nav.addClass('minimized') }) $('.navbutton').on('click', function(event) { if ($(this).attr('href') == "#contact") $('html, body').stop().animate({ scrollTop: documentHeight }, 300, 'swing') else $('html, body').stop().animate({ scrollTop: $($(this).attr('href')).offset().top }, 300, 'swing') event.preventDefault() }) })
The best thing to do is to create a class to handle all the element functions. for example, let's assume that you have a div or a canvas that you want to check for it's size. function divHandler(divID){ this.div = document.getElementById(divID); //Add all global variables here this.boundingRect = this.div.getBoundingClientRect() } //After that access the variables by other functions divHandler.prototype.displayData = function(){ console.log(this.boundingRect); } const d = new divHandler('someDiv'); d.displayData(); #someDiv{ min-width: 100px; min-height: 100px; border: 1px solid black; } <div id="someDiv"> </div> Now you have a class that controls all divs by id and you can use it again and again for all other divs in the code. Update 1: You can also add an event listener like this function divHandler(divID){ this.div = document.getElementById(divID); //Add all global variables here this.boundingRect = this.div.getBoundingClientRect() } divHandler.prototype.listen = function (event, cb, ...args){ this.div.addEventListener(event,cb.bind(this, ...args)); } const d = new divHandler('someDiv'); d.listen('click', function(e){ console.log(e.type); this.div.style.backgroundColor = "blue" }); #someDiv{ min-width: 100px; min-height: 100px; border: 1px solid black; } <div id="someDiv"></div>
Reactjs - Inlinestyle "top" and "left"
I'm trying to align different circles around the perimeter of a larger circle, and in order to do that I must adjust top: and left: for each div. I'm trying to do this inline in the following way: for(var i = 0; i < n_circles; i++){ var posx = divHeigth/2 + Math.round(radius * (Math.cos(theta[i]))) + "px"; var posy = divHeigth/2 - Math.round(radius * (Math.sin(theta[i]))) + "px"; console.log("X: " + posx + " Y: " + posy) var scope = { position: "absolute", top: "300px", left: {posx}, backgroundColor: "white" } var circle = <div className="circles" style={{scope}}></div> circles.push(circle) } However if I replace {top} with hard coded text like "300px" it works fine. As you can see I log posx and posy and their values look fine(300px, 200px etc..) But if I use the variables, it doesnt even get registered in Developers tool? How can I style top and left inline in Reactjs?
for(var i = 0; i < n_circles; i++){ var posx = divHeigth/2 + Math.round(radius * (Math.cos(theta[i]))) + "px"; var posy = divHeigth/2 - Math.round(radius * (Math.sin(theta[i]))) + "px"; console.log("X: " + posx + " Y: " + posy) var scope = { position: "absolute", top: "300px", left: {posx}, <---- this is wrong, should simply be "left: posx," backgroundColor: "white" } var circle = <div className="circles" style={{scope}}></div> circles.push(circle) } You do not need the curly brackets around your variable as you are applying it to JS portion of your your code (not HTML/JSX).
Svg Draw flickering happening in Chrome browser
I need to allow user to draw a shape and I have used svg for same. In other browser the code is working perfectly fine, but in case of chrome Version 52.0.2743.116 m it is flickering. When I created it it was working fine. But as chrome was updated the issue started happening Issue is not replicable in chrome Version 49.0.2623.110 m https://jsfiddle.net/2svxmgwu/1/ Try to drag and drop in yellow area from left top side to right bottom you will see barcode Refer the attached image The flickering does not happen if direction of creating shape is other then from top left to lower bottom. Code Html <div class="thisComp"> <div class="svgElementDiv"> </div> </div> Js var svgShape = $('<svg viewBox="0 0 640 480" preserveAspectRatio="none" xmlns="http://www.w3.org/2000/svg"><g><title>Layer 1</title> <rect stroke="#5B9BD5" id="svg_11" height="473.99998" width="635.99997" y="3" x="3" stroke-linecap="null" stroke-linejoin="null" preserveAspectRatio="none" vector-effect="non-scaling-stroke" stroke-dasharray="null" stroke-width="3" fill="#5B9BD5"/> </g></svg>'); var self = this; //Added for chrome browser as without this flickering is very high svgShape.find('[vector-effect="non-scaling-stroke"]').removeAttr('vector-effect'); var $ShapeWrapper = $(".svgElementDiv"); var $thisComp = $(".thisComp"); svgShape.css({ position: "absolute", left: "0", top: "0", width: "100%", height: "100%", opacity: "0.5" }); $ShapeWrapper.css({ position: "fixed" }); $thisComp.mousedown(function (event) { if ($ShapeWrapper.length > 0) { $ShapeWrapper.html(svgShape); $ShapeWrapper.css("left", event.clientX); $ShapeWrapper.css("top", event.clientY); self._selectorLeftPos = event.clientX; self._selectorTopPos = event.clientY; $ShapeWrapper.css({ width: "0", height: "0" }); self._dragged = false; event.preventDefault(); } }); var removeShape = function (event) { if ($ShapeWrapper.find(svgShape).length > 0 && !(event.type=="mouseleave" && $(event.relatedTarget).closest('.guideWrapper').length>0)) { var startX = (($ShapeWrapper.offset().left - $thisComp.offset().left) / self._transformRatio) / self._conversionFactor; var startY = (($ShapeWrapper.offset().top - $thisComp.offset().top) / self._transformRatio) / self._conversionFactor; var selectWidth = ($ShapeWrapper.width() / self._transformRatio) / self._conversionFactor; var selectHeight = ($ShapeWrapper.height() / self._transformRatio) / self._conversionFactor; self._shapeData = '{"left":"' + startX + '","top":"' + startY + '","height":"' + selectHeight + '","width":"' + selectWidth + '"}'; svgShape.remove(); } } $thisComp.mousemove(function (event) { if ($ShapeWrapper.length > 0) { var width = 0; var height = 0; if (event.clientX <= self._selectorLeftPos) { width = $ShapeWrapper.offset().left - event.clientX + $ShapeWrapper.width(); $ShapeWrapper.css("left", event.clientX); } else { width = event.clientX - $ShapeWrapper.offset().left; } if (event.clientY <= self._selectorTopPos) { height = $ShapeWrapper.offset().top - event.clientY + $ShapeWrapper.height(); $ShapeWrapper.css("top", event.clientY); } else { height = event.clientY - $ShapeWrapper.offset().top; } $ShapeWrapper.css("width", width); $ShapeWrapper.css("height", height); if (width > 3 || height > 3) { self._dragged = true; } } }); $thisComp.bind("mouseup", removeShape); $thisComp.bind("mouseleave", removeShape); Css .thisComp{ height:400px; width:400px; background-color:yellow; }
Make words pulsate jQuery
http://codepen.io/anon/pen/ZGBEyo I have a difficult task as you saw in the title of the question. I'm doing a plugin that makes the words move in the screen, increase in size with a sharpen effect, and decrease in size with a blur effect, and the most difficult — one respect other if they have the same name, you may ask: what do you mean? They have this totally random behavior to grow and stay focus, get smaller and become blurred, however I need make two words with the same name never become clear at same time. My plugin works like this: <span class="word" data-float="true" data-range-x="100" data-range-y="100" data-top="500" data-left="50" data-size="25">Love</span> If data-float is true, the word is authorised to use the plugin data-range-x = the maximum x range that can move data-range-y = the maximum y range that can move data-top = the top initial position data-left = the left initial position data-size = the size of the word So, is possible to make them respect each other, if they have the same name? Working plugin it three love words: + function($) { var Float = function(element) { this.element = element; this.wrapper; this.settings = { rangeX : element.data('range-x') || 100, rangeY : element.data('range-y') || 100, speed : element.data('speed') || 5000, top : element.data('top') || 0, left : element.data('left') || 0, size : element.data('size') || 20 }; }; Float.prototype.init = function() { this.setPosition(); this.setSize(); this.setWrapper(); this.andWalk(); }; Float.prototype.setPosition = function() { this.element .css('top', this.settings.top) .css('left', this.settings.left); }; Float.prototype.setSize = function() { this.element.css('font-size', this.settings.size + 'px'); if (this.settings.size <= 20) this.setShadow(); }; Float.prototype.setShadow = function() { console.log('test'); this.element.css('color', 'transparent'); }; Float.prototype.setWrapper = function() { this.wrapper = $('<div/>').css({ 'width': this.element.outerWidth(true), 'height': this.element.outerHeight(true), 'z-index': this.element.css('zIndex') }); this.wrapper.css({ 'position': this.element.css('position'), 'top': this.element.position().top, 'left': this.element.position().left, 'float': this.element.css('position') === 'absolute' ? 'none' : 'left' }); this.element.wrap(this.wrapper).css({ 'position': 'absolute', 'top': 0, 'left': 0 }); }; Float.prototype.andWalk = function() { var self = this; var newX = Math.floor(Math.random() * this.settings.rangeX) - this.settings.rangeX / 2; var newY = Math.floor(Math.random() * this.settings.rangeY) - this.settings.rangeY / 2 - 0; var newS = Math.floor(Math.random() * this.settings.speed) + this.settings.speed / 2; var newF; if (this.settings.size > 40) { newF = Math.floor(Math.random() * (70 - 15 + 1) + 15); } else if (this.settings.size <= 25) { newF = Math.floor(Math.random() * (25 - 15 + 1) + 15); } else { newF = Math.floor(Math.random() * (40 - 15 + 1) + 15); } this.element.animate({ 'fontSize': newF, 'color': newF >= 25 ? '#FFFFFF' : 'transparent', 'top': newY, 'left': newX }, newS, function() { self.andWalk(); }); }; $(window).on('load', function() { $('[data-float]').each(function() { var element = $(this).data('float') || false; if (element) { var float = new Float($(this)); float.init(); } }); }); }(jQuery); body { background: black; color: white; } .word { position: absolute; text-shadow: 0 0 5px rgba(255, 255, 255, 0.9); } <span class="word" data-float="true" data-index="1" data-range-x="100" data-range-y="100" data-top="500" data-left="50" data-size="25">Love</span> <span class="word" data-float="true" data-index="2" data-range-x="100" data-range-y="100" data-top="90" data-left="290" data-size="40">Love</span> <span class="word" data-float="true" data-index="3" data-range-x="100" data-range-y="100" data-top="500" data-left="290" data-size="70">Love</span> <script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script> <script src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js"></script>
You may want to leverage the animate.css library as it uses CSS3 transitions to animate elements with pulse, flip, zoom, rotate, roll, fade and other effects. Then you could use jquery or pure javascript to call these effects. Here is a jsfiddle I made to demonstrate how you can call the effects from this library with jQuery http://jsfiddle.net/ashk3l/qxfj8L2d/ Essentially your HTML could look something like this: <h1 class="your-element">Hello!</h1> And your jQuery call could look something like this: $(document).ready(function () { $('.your-element').addClass('animated pulse'); });
Drag and Drop -JQuery
Im creating myself a drag functions just for my personal use so I can drag elements around. I'm having a problem. At the moment trying to make the drag so one you pick up the element you can drag it but once you drop it, it goes back to the original position. This is not working as you can see here. When I let go of #drag2 or #drag3 then it goes to the position of #drag1. My Function: function drag(el) { var position = $(el).position(); var ptop = position.top; var pleft = position.left; var down = false; $(el).mousedown(function(event) { down = true; $(this).css({ cursor: 'crosshair', }); $(this).mousemove(function(event) { if (down == true) { $(this).css({ cursor: 'crosshair', }); var w = $(this).width(); var h = $(this).height(); var left3 = (w / 2) + 7; var top3 = (h / 2) + 7; $(this).css({ cursor: 'crosshair', left: (event.clientX) + (3 * 3) - left3, top: (event.clientY) + (3 * 3) - top3 }); } }).mouseup(function() { down = false; $(this).css({ cursor: 'default', }); $(this).animate({ top: ptop, left: pleft }, 300); }); }); } I have to get the old position: var position = $(el).position(); var ptop = position.top; var pleft = position.left; So should it not get the position of all of them and bring them self back to where they were? Any help will be appreciated. EDIT: I DO NOT WANT TO USE ANY PLUGIN OR JQUERY UI, THANKS ANYWAYS
The solution is very simple See this fiddle: http://jsfiddle.net/BggPn/4/ you define 1 var for left and 1 for top. All 3 the elements use the same variables. If you loop through the elements then you make a new scope where each element has it's own vars. Working code: $(document).ready(function() { drag('#drag, #drag2, #drag3') }); function drag(el) { $(el).each(function(){ var position = $(this).position(); var ptop = position.top; var pleft = position.left; var down = false; $(this).mousedown(function(event) { down = true; $(this).css({ cursor: 'crosshair', }); $(this).mousemove(function(event) { if (down == true) { $(this).css({ cursor: 'crosshair', }); var w = $(this).width(); var h = $(this).height(); var left3 = (w / 2) + 7; var top3 = (h / 2) + 7; $(this).css({ cursor: 'crosshair', left: (event.clientX) + (3 * 3) - left3, top: (event.clientY) + (3 * 3) - top3 }); } }).mouseup(function() { down = false; $(this).css({ cursor: 'default', }); $(this).animate({ top: ptop, left: pleft }, 300); }); }); }); } Make it a plugin: $.fn.drag = function drag(){ return this.each(function(){ var position = $(this).position(); var ptop = position.top; var pleft = position.left; var down = false; $(this).mousedown(function(event) { down = true; $(this).css({ cursor: 'crosshair', }); $(this).mousemove(function(event) { if (down == true) { $(this).css({ cursor: 'crosshair', }); var w = $(this).width(); var h = $(this).height(); var left3 = (w / 2) + 7; var top3 = (h / 2) + 7; $(this).css({ cursor: 'crosshair', left: (event.clientX) + (3 * 3) - left3, top: (event.clientY) + (3 * 3) - top3 }); } }).mouseup(function() { down = false; $(this).css({ cursor: 'default', }); $(this).animate({ top: ptop, left: pleft }, 300); }); }); }); } Now you can call it by: $("#drag1, #drag2").drag();
2 thoughts: 1.) Why don't you use jQuery UI (draggable)? 2.) The idea of giving a selector in stead of an element is wrong, a selector can lead to multiple elements, and that's the first reason why it's failing. If you start with function drag(selector) { $(selector).each(function() { var el = $(this); //rest of code } } it would be better. But I think you'll run into a few other problems eventually
The problem is whenever you're sending in the list of selectors '#drag, #drag2. #drag3' and your position variable only cares about the first one (try changing the order and you'll see they snap to the first in the list). If you want to go about it this way then you'll want to perform an each iteration over them to get the right values.
get the current position of div after drag and set via this .mouseup(function() { var position = $(this).position(); down = false; $(this).css({ cursor: 'default', }); $(this).animate({ top: position.top, left: position.left }, 300); DEMO why don't you use drag and drop plugin. see this article http://viralpatel.net/blogs/2009/05/implement-drag-and-drop-example-jquery-javascript-html.html or this one http://plugins.jquery.com/project/dragndrop