如何使用jquery插件Full Page js制作<a> element with text-selectable child elements?

时间:2018-12-05 18:04:54

标签: javascript html css copy-paste dom-events

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 &amp; 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:

  • The full <a> element must be clickable as a link, except for the company ID <span>.
  • The full <a> element can be middle-clicked to open it in a new tab, except for the company ID <span>.
  • A user must be able to click-and-drag on the company ID <span> to select and copy its text.
  • A user must be able to double-click on the company ID <span> to select and copy its text.
  • The layout must be flexible, allowing text spans of arbitrary length, and collapsing to the narrowest overall width.
  • The solution must work in modern evergreen browsers (i.e., Chrome, Firefox, Edge — no old-IE compatibility required!).

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?

2 个答案:

答案 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 &amp; 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>');
  });