使用书签创建弹出菜单
Creating a popup menu with bookmarklets
这是我的第一个问题,所以请指点我如何提出更好的问题。
无论如何,我将如何制作一个显示链接的可切换菜单,该菜单使用书签激活。我试图找到答案,但都没有结果。我需要为此创建一个新元素吗?
您将需要使用 vanilla JS 创建弹出菜单。我还实现了拖动功能。唯一需要做的就是在滚动页面时正确设置位置。
DOM布局
需要以下最重要的元素和样式。
<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>
<a href="https://www.google.com" target="_blank">Google</a>
</li>
<li>
<a href="https://www.bing.com" target="_blank">Bing</a>
</li>
<li>
<a href="https://duckduckgo.com" target="_blank">DuckDuckGO</a>
</li>
</ul>
</div>
</div>
您可以保存以下小书签:
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);
缩小和混淆的方法
- 我使用以下代码缩小了以下代码:https://javascript-minifier.com/
- 我使用以下方法混淆了生成的缩小代码:https://obfuscator.io/
对于混淆器,我将 "Identifier Names Generator" 设置为 "mangled" 并检查了 "Rename Globals"。
重要提示: 取消选择 "String Array" 否则您无法添加新的 link 条目。
注意事项
脚本不允许超出初始宽度和高度。可以合并 this post 中的以下宽度和高度方法来修复此限制。
源代码
(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);
改善
您会注意到,如果您的光标在拖动时离开屏幕(并且您松开按钮),window 将卡在拖动中。您可以在全球范围内检测到这一点,但您还需要弄清楚如何将位置重新初始化为最后已知的 "good" 位置。
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';
}
}
}
最后,不要向小书签按钮发送垃圾邮件,因为它会创建相同 window 的多个实例。可以添加代码以在创建新代码之前检测 window 的存在。关闭它可以隐藏它,所以它只会使现有的再次可见。多个 windows 将打破关闭监听器。
这是我的第一个问题,所以请指点我如何提出更好的问题。 无论如何,我将如何制作一个显示链接的可切换菜单,该菜单使用书签激活。我试图找到答案,但都没有结果。我需要为此创建一个新元素吗?
您将需要使用 vanilla JS 创建弹出菜单。我还实现了拖动功能。唯一需要做的就是在滚动页面时正确设置位置。
DOM布局
需要以下最重要的元素和样式。
<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>
<a href="https://www.google.com" target="_blank">Google</a>
</li>
<li>
<a href="https://www.bing.com" target="_blank">Bing</a>
</li>
<li>
<a href="https://duckduckgo.com" target="_blank">DuckDuckGO</a>
</li>
</ul>
</div>
</div>
您可以保存以下小书签:
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);
缩小和混淆的方法
- 我使用以下代码缩小了以下代码:https://javascript-minifier.com/
- 我使用以下方法混淆了生成的缩小代码:https://obfuscator.io/
对于混淆器,我将 "Identifier Names Generator" 设置为 "mangled" 并检查了 "Rename Globals"。
重要提示: 取消选择 "String Array" 否则您无法添加新的 link 条目。
注意事项
脚本不允许超出初始宽度和高度。可以合并 this post 中的以下宽度和高度方法来修复此限制。
源代码
(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);
改善
您会注意到,如果您的光标在拖动时离开屏幕(并且您松开按钮),window 将卡在拖动中。您可以在全球范围内检测到这一点,但您还需要弄清楚如何将位置重新初始化为最后已知的 "good" 位置。
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';
}
}
}
最后,不要向小书签按钮发送垃圾邮件,因为它会创建相同 window 的多个实例。可以添加代码以在创建新代码之前检测 window 的存在。关闭它可以隐藏它,所以它只会使现有的再次可见。多个 windows 将打破关闭监听器。