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.
So basically... as I scroll down a page, when it hits a certain point on the scroll, I want my button to scroll down the page to the bottom. Here is what I have so far.
I know the detection of when to do the action works because un-commenting out the alert works at the correct position. I need help with the button being scrolled down.
$(document).ready(function() {
$(window).scroll(function() {
var height = $(window).scrollTop();
if(height > 1000) {
//alert("hit part of page i want... works!");
$('#<%= btnFinish.ClientID %>').animate({ scrollTop: $(document).height() }, 500);
}
});
EDIT: HOW TO DO:
$(window).scroll(function() {
var d;
var i;
var top = $(window).scrollTop();
var windowHeight = $(window).height();
var docHeight = $(document).height()
var obj = $('#<%= btnFinish.ClientID %>');
var bottom = obj.position().top + obj.height();
if (top + windowHeight == docHeight && bottom <= 800) {
d = 0;
for(i = 410; i>=85; i=i-.1){
d += .15;
(function(ii,dd){
setTimeout(function(){
$('#<%= btnFinish.ClientID %>').css({ bottom: ii, right: 460 });
}, dd);
})(i,d);
}
} else if (top == 0 && bottom >= 801) {
d = 0;
for(i = 85; i<=410; i=i+.1){
d += .15;
(function(ii,dd){
setTimeout(function(){
$('#<%= btnFinish.ClientID %>').css({ bottom: ii, right: 460 });
}, dd);
})(i,d);
}
}
});
You can fix its position with CSS style.
style="position: fixed; bottom: 10px; right: 10px;"
And if you want to make it display after specific scroll then just show/hide that button after some scroll as per your convenient.
You can use CSS transition:
$( document ).ready( function () {
$( window ).scroll( function () {
var height = $( window ).scrollTop();
if ( height < 500 ) {}
if ( height > 1000 ) {
$('#test').css({ bottom: 10, right: 10 });
} else {
$('#test').css({ bottom: 170, right: 10 });
}
} );
} );
body {
position: relative;
height: 2000px
}
#test {
position: fixed;
bottom: 170px;
right: 10px;
-webkit-transition: all 1s;
-moz-transition: all 1s;
-o-transition: all 1s;
transition: all 1s
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<button type="button" id="test">Button</button>
$(window).scroll(function() {
var d;
var i;
var top = $(window).scrollTop();
var windowHeight = $(window).height();
var docHeight = $(document).height()
var obj = $('#<%= btnFinish.ClientID %>');
var bottom = obj.position().top + obj.height();
if (top + windowHeight == docHeight && bottom <= 800) {
d = 0;
for(i = 410; i>=85; i=i-.1){
d += .15;
(function(ii,dd){
setTimeout(function(){
$('#<%= btnFinish.ClientID %>').css({ bottom: ii, right: 460 });
}, dd);
})(i,d);
}
} else if (top == 0 && bottom >= 801) {
d = 0;
for(i = 85; i<=410; i=i+.1){
d += .15;
(function(ii,dd){
setTimeout(function(){
$('#<%= btnFinish.ClientID %>').css({ bottom: ii, right: 460 });
}, dd);
})(i,d);
}
}
});
I have the following jQuery and JS code:
$(function() {
function addMouseEvents() {
$('img').addClass('close-overlay-active')
var close_H = ($('.close-overlay-active').height() * 1.2);
var close_W = ($('.close-overlay-active').width() * 1.2);
document.addEventListener('mousemove', function(e) {
// console.log('move');
// console.log(e.pageX + " " + e.pageY );
var mouseX = e.pageX,
mouseY = e.pageY;
$('.close-overlay-active')
.css({
top: (mouseY - close_H),
left: mouseX - close_W
});
});
}
addMouseEvents();
});
The desired behaviour ofcourse is for the 'x' mark to be slightly above the cursor. That does happen most of the time , but sometimes the 'x' appears below the cursor that is towards the right bottom of the cursor and sometimes 1000px below the cursor. Why is this happening ? e.pageY seems to be returning the wrong value sometimes Why ?
Can anybody please explain this behavior please ?
FIDDLE HERE
Try this:
$(function() {
function addMouseEvents() {
$('img').addClass('close-overlay-active')
var close_H = ($('.close-overlay-active').height());
var close_W = ($('.close-overlay-active').width());
document.addEventListener('mousemove', function(e) {
// console.log('move');
// console.log(e.pageX + " " + e.pageY );
var mouseX = e.pageX,
mouseY = e.pageY;
$('.close-overlay-active')
.css({
top: (mouseY - close_H),
left: mouseX - close_W / 2
});
});
}
addMouseEvents();
});
img {
position: absolute;
top: 0;
left: 0;
}
.fixed {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #b0e2ff;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<div class="fixed">
<img src="https://cdn3.iconfinder.com/data/icons/sympletts-free-sampler/128/circle-close-128.png">
</div>
To get cursor exactly in the center, refer this FIDDLE
I have this jQuery code for a carousel but its not working as I supposed it should.
var totalItems = $('.inner li').length,
currentItem = 1,
$inner = $('.inner li'),
width = $inner.outerWidth(true);
setInterval(function() {
$inner.animate({
right: '+=' + width
}, 500);
currentItem += 1;
}, 500);
You can view the example at this FIDDLE
Thanks in advance.
EDIT:
This is the entire code I have working for another carousel.
$(document).ready(function() {
var totalItems = $('.inner li').length,
currentItem = 1,
$inner = $('.inner li'),
width = $inner.outerWidth(true),
speed = 400,
delay = 4000;
var timer = setInterval(function() {
if (currentItem === totalItems) {
$inner.animate({
right: '-=' + width * (totalItems-1) + 'px'
}, speed);
currentItem = 1;
} else {
$inner.animate({
right: '+=' + width
}, speed);
currentItem += 1;
}
}, delay);
$('.carousel').hover (function(ev) {
clearInterval(timer);
}, function(ev){
timer = setInterval(function() {
if (currentItem === totalItems) {
$inner.animate({
right: '-=' + width * (totalItems-1) + 'px'
}, speed);
currentItem = 1;
} else {
$inner.animate({
right: '+=' + width
}, speed);
currentItem += 1;
}
}, delay);
});
$('#right').click(function () {
if (currentItem === totalItems) {
$inner.animate({
right: '-=' + width * (totalItems-1) + 'px'
}, speed);
currentItem = 1;
} else {
$inner.animate({
right: '+=' + width,
}, speed);
currentItem += 1;
}
});
$('#left').click(function () {
if (currentItem === 1) {
$inner.animate({
right: '+=' + width * (totalItems-1) + 'px'
}, speed);
currentItem = totalItems;
} else {
$inner.animate({
right: '-=' + width
}, speed);
currentItem -= 1;
}
});
});
No, it doesn't.
It just set's up an interval at which the passed function is called, so if you pass 500ms as an interval, the function will be called (about) 500ms from now, then (about) 1 second from now, then ...
setInterval also has nothing to do with page load, if that is what you meant (e.g. it doesn't depend on body load or anything like that, but the moment it gets executed)
I'm saying about because browser timing isn't very exact (for several reasons). Also, you should save the returned value of setInterval in a variable, and make sure to clean up the interval when you don't need it anymore. Depending on browser, this is normally done automatically on page unload, but on some older browsers it might not happen until the browser is closed.
Edit: Looking at the fiddle, your problem is with css, not with javascript. Your li need to be positioned relative or absolute, otherwise setting right property will have no effect. You could also set margin-right/margin-left.
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