I inherited some markup where a series of top-level <a>
elements each contain a set of <span>
elements, and using CSS, they're rendered as clickable blocks in a list, like this:
.list {
display: inline-flex;
flex-flow: column nowrap;
font: 14px Arial;
}
.list a {
display: flex;
flex-flow: column nowrap;
align-items: stretch;
border: 1px solid #CCC;
border-bottom: none;
background: #FFF;
padding: 4px 10px;
text-decoration: none;
color: #000;
}
.list a:last-child {
border-bottom: 1px solid #CCC;
}
.list a:hover {
background: #CDE;
}
.list a .name {
font-weight: bold;
}
.list a .secondary {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
color: #678;
font-size: 85%;
padding-top: 2px;
}
.list a .address {
padding-right: 16px;
padding-left: 8px;
}
.list a .company-id {
color: #B88;
cursor: text;
padding-left: 4px;
padding-right: 4px;
margin-right: -4px;
}
<div class="list">
<a href="/link/to/company/10101">
<span class="name">Alice Jones & Co.</span>
<span class="secondary">
<span class="address">55 Oak Street, Anytown 15151</span>
<span class="company-id">#10101</span>
</span>
</a>
<a href="/link/to/company/12345">
<span class="name">John Smith Inc.</span>
<span class="secondary">
<span class="address">123 Main Street, Anytown 15151</span>
<span class="company-id">#12345</span>
</span>
</a>
<a href="/link/to/company/20123">
<span class="name">Bob Johnson LLC</span>
<span class="secondary">
<span class="address">17 Spruce Street, Anytown 15152</span>
<span class="company-id">#20123</span>
</span>
</a>
</div>
The Request
A product owner asked me the other day if I could make the company IDs not clickable — our users want to be able to select the text of the IDs for copy-and-paste. Fine, I thought: Turn each <a>
element into an <li>
like it should be anyway, add a little JavaScript to follow the links on clicks, and ignore clicks on the company IDs, and done.
Then I learned there's another user requirement — that the <a>
elements must also be middle-clickable or Ctrl-clickable to open them in a new tab. I intended to tweak the JavaScript to invoke window.open()
if the Ctrl-key or middle-mouse button was down, but it seems that ad-blockers and browser popup blockers get in the way of that working reliably: The <a>
element needs to be a real <a>
element, and its events must be left more-or-less untouched. But that means that the <a>
will capture every bubbling event on its content, including events I'd prefer it not touch, like the click-and-drag (and double-click) events on the company ID.
And since the list has a flexible layout, I can't put the company ID element outside the <a>
element, and then appear to make it part of the same block using position
or margin
tricks: The spacing won't work, because the IDs vary pretty widely in length (from 1
to 129370-5486
).
tl;dr: I need a child element to exist inside an <a>
element for layout — but it needs to exist outside the same element for behavior.
Requirements
For a valid solution, I have to meet these requirements:
<a>
element must be clickable as a link, except for the company ID <span>
.<a>
element can be middle-clicked to open it in a new tab, except for the company ID <span>
.<span>
to select and copy its text.<span>
to select and copy its text.Beyond that, the sky's the limit: Dependencies, no dependencies, add/tweak the CSS, add some JS, change the markup — as long as those six bullet points are met, you can do whatever you want.
My Best Solution
I've tried an awful lot of JavaScript event-capturing tricks so far, most of which were failures. The best working solution I've found involves no JS at all: I include the company ID in the markup twice — once inside the <a>
with visibility:hidden
for layout purposes, and then again in the markup after the </a>
, with a position:relative
-containing <li>
element around all of it, and position:absolute / bottom: / right:
on the visible, selectable <span>
. But it seems like there ought to be a better way that doesn't involve mutating the markup; and if the product owners ever want more text in each box, or a slightly different layout, my solution is not likely to adjust to those changes very well.
So do you have any better ideas than I have for pulling off normal, selectable text elements inside an otherwise-clickable <a>
element parent?
答案 0 :(得分:3)
海顿·皮克林(Heydon Pickering)在inclusive card design上发表了一篇文章,其中介绍了如何处理可单击整个卡片的卡片组件内的可选文本。
最适合您的解决方案之一是将每个<a>
元素更改为<li>
元素,然后在名称周围添加一个<a>
标签该公司的。然后,您可以在锚标记中添加一个伪元素,以扩展父<li>
的整个宽度/高度。最后,将position: relative
添加到ID中,使其位于anchors伪元素上方,这样它就不会激活链接。
.list {
display: inline-flex;
flex-flow: column nowrap;
font: 14px Arial;
margin: 0;
padding: 0;
}
.list li {
display: flex;
flex-flow: column nowrap;
align-items: stretch;
border: 1px solid #CCC;
border-bottom: none;
background: #FFF;
padding: 4px 10px;
text-decoration: none;
color: #000;
position: relative;
}
.list a::after {
content: '';
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
.list li:last-child {
border-bottom: 1px solid #CCC;
}
.list li:hover {
background: #CDE;
}
.list li .name {
font-weight: bold;
}
.list li .secondary {
display: flex;
flex-flow: row nowrap;
justify-content: space-between;
color: #678;
font-size: 85%;
padding-top: 2px;
}
.list li .address {
padding-right: 16px;
padding-left: 8px;
}
.list li .company-id {
color: #B88;
cursor: text;
padding-left: 4px;
padding-right: 4px;
margin-right: -4px;
position: relative;
}
<ul class="list">
<li>
<a href="/link/to/company/10101" class="name">Alice Jones & Co.</a>
<span class="secondary">
<span class="address">55 Oak Street, Anytown 15151</span>
<span class="company-id">#10101</span>
</span>
</li>
<li>
<a href="/link/to/company/12345" class="name">John Smith Inc.</a>
<span class="secondary">
<span class="address">123 Main Street, Anytown 15151</span>
<span class="company-id">#12345</span>
</span>
</li>
<li>
<a href="/link/to/company/20123" class="name">Bob Johnson LLC</a>
<span class="secondary">
<span class="address">17 Spruce Street, Anytown 15152</span>
<span class="company-id">#20123</span>
</span>
</li>
</ul>
答案 1 :(得分:0)
这是我创建的相关codepen,但是在js中注释了psuedo-a:https://codepen.io/anon/pen/XyLgag
这应该可以帮助您入门,但我不能完全按照您的需要。...HTH
CSS:
a.psuedo {
position: absolute;
top: 0;
left: 30%;
width: 15px;
height: 15px;
border: 0 !important;
background: transparent;
margin: 0;
padding: 0;
}
要复制到剪贴板的查询:
//Add custom event listener
$(':root').on('mousedown', '*', function() {
var el = $(this),
events = $._data(this, 'events');
if (events && events.clickHold) {
el.data(
'clickHoldTimer',
setTimeout(
function() {
el.trigger('clickHold')
},
el.data('clickHoldTimeout')
)
);
}
}).on('mouseup mouseleave mousemove', '*', function() {
clearTimeout($(this).data('clickHoldTimer'));
});
//Attach it to the element
$('.company-id').data('clickHoldTimeout', 2000); //Time to hold
$('.company-id').on('clickHold', function() {
/* Get the text field */
var copyText = $(this).html();
/* Copy the text inside the text field */
document.execCommand("copy");
console.log("copied -- " + copyText);
});
添加伪锚标签的查询
$(".company-id").each(function() {
$(this).append('<a class="psuedo" target="_blank" href="www.google.com"></a>');
});