Pen Settings

HTML

CSS

CSS Base

Vendor Prefixing

Add External Stylesheets/Pens

Any URL's added here will be added as <link>s in order, and before the CSS in the editor. If you link to another Pen, it will include the CSS from that Pen. If the preprocessor matches, it will attempt to combine them before processing.

+ add another resource

JavaScript

Babel includes JSX processing.

Add External Scripts/Pens

Any URL's added here will be added as <script>s in order, and run before the JavaScript in the editor. You can use the URL of any other Pen and it will include the JavaScript from that Pen.

+ add another resource

Packages

Add Packages

Search for and use JavaScript packages from npm here. By selecting a package, an import statement will be added to the top of the JavaScript editor for this package.

Behavior

Save Automatically?

If active, Pens will autosave every 30 seconds after being saved once.

Auto-Updating Preview

If enabled, the preview panel updates automatically as you code. If disabled, use the "Run" button to update.

Format on Save

If enabled, your code will be formatted when you actively save your Pen. Note: your code becomes un-folded during formatting.

Editor Settings

Code Indentation

Want to change your Syntax Highlighting theme, Fonts and more?

Visit your global Editor Settings.

HTML

              
                <div id="pop" role="tooltip">
  <div class="popper">
    <i class="fa fa-bold"></i>
    <i class="fa fa-italic"></i>
    <i class="fa fa-link"></i>
    <div x-arrow></div>
  </div>
</div>
<div class="editor" contentEditable>
  <h1>Attach tooltips to text selection</h1>
  <p>
    <a href="https://popper.js.org">Popper.js</a> can be used to position anything, even tooltips that need to be positioned near a selected text!<br />
    Try to select part of this article to see a tooltip appear near it.
  </p>
  <p>
    You can also try to select a text, then select something else, and see the tooltip automatically update to being properly positioned.<br />
    If you scroll this text while the tooltip is visible, you'll notice how it will automatically flip to stay between the viewport.
  </p>
  <hr />
<p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p><p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
  <p>
  There are many variations of passages of Lorem Ipsum available, but the majority have suffered alteration in some form, by injected humour, or randomised words which don't look even slightly believable. If you are going to use a passage of Lorem Ipsum, you need to be sure there isn't anything embarrassing hidden in the middle of text. All the Lorem Ipsum generators on the Internet tend to repeat predefined chunks as necessary, making this the first true generator on the Internet. It uses a dictionary of over 200 Latin words, combined with a handful of model sentence structures, to generate Lorem Ipsum which looks reasonable. The generated Lorem Ipsum is therefore always free from repetition, injected humour, or non-characteristic words etc.
  </p>
</div>
              
            
!

CSS

              
                @use postcss-nested;

#pop {
  transition: transform 0.2s ease-in-out;
  .popper {
    background-image: linear-gradient(
      to bottom,
      rgba(49, 49, 47, 0.99),
      #262625
    );
    background-repeat: repeat-x;
    border-radius: 5px;
    padding: 0 10px;
    color: white;
    line-height: 44px;
    display: inline-block;
    opacity: 0;
    pointer-events: none;
    
    &--visible {
      pointer-events: auto;
      animation: pop-upwards 180ms forwards linear;
      animation-delay: 0.2s;
      transition: opacity 0s linear 0.2s;
    }
  }

  &[x-placement="top"] {
    margin-bottom: 7px;
  }
  &[x-placement="bottom"] {
    margin-top: 7px;
  }
  &[x-placement="bottom"] .popper {
    background-image: linear-gradient(to top, rgba(49, 49, 47, 0.99), #262625);
  }
}

[x-arrow] {
  position: absolute;
  width: 14px;
  height: 14px;
  background-color: #262625;
  transform: rotate(45deg);
  z-index: -1;

  [x-placement="top"] & {
    margin-bottom: -7px;
    bottom: 0;
  }
  [x-placement="bottom"] & {
    margin-top: -7px;
    top: 0;
  }
}


@keyframes pop-upwards {
  0% {
    transform: matrix(0.97, 0, 0, 1, 0, 12);
    opacity: 0;
  }

  20% {
    transform: matrix(0.99, 0, 0, 1, 0, 2);
    opacity: 0.7;
  }

  40% {
    transform: matrix(1, 0, 0, 1, 0, -1);
    opacity: 1;
  }

  70% {
    transform: matrix(1, 0, 0, 1, 0, 0);
    opacity: 1;
  }

  100% {
    transform: matrix(1, 0, 0, 1, 0, 0);
    opacity: 1;
  }
}

/* Editor styling */
.editor {
  max-width: 700px;
  margin-left: auto;
  margin-right: auto;
  font-family: Lora, sans-serif;
  font-size: 21px;
  line-height: 1.58;
  &:focus {
    outline: 0;
  }
}


i {
  width: 1.4em;
  text-align: center;
}

hr {
  border: 0;
  &::before {
    content: ". . .";
    color: rgba(0, 0, 0, 0.68);
    text-align: center;
    display: block;
    width: 100%;
    font-size: 1.5em;
    position: relative;
    top: -7px;
    letter-spacing: 0.1em;
  }
}

a {
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  background-image: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.68) 50%,
    rgba(0, 0, 0, 0) 50%
  );
  background-repeat: repeat-x;
  background-size: 2px 0.1em;
  background-position: 0 1.07em;
}


              
            
!

JS

              
                class RangeRef {
  constructor() {
    this.updateRect();

    const update = (evt, hide) => {
      let selection = document.getSelection();

      this.range = selection && selection.rangeCount && selection.getRangeAt(0);

      this.updateRect(hide);
    }
    document
      .querySelector("[contenteditable]")
      .addEventListener("mouseup", update);
    document
      .querySelector("[contenteditable]")
      .addEventListener("input", update);
    document
      .querySelector("[contenteditable]")
      .addEventListener("keydown", evt => update(evt, true));

    window
      .addEventListener("scroll", update);
    document
      .scrollingElement
      .addEventListener("scroll", update);
  }

  updateRect(hide) {
    if (!hide && this.range) {
      this.rect = this.range.getBoundingClientRect();
    } else {
      this.rect = {
        top: 0,
        left: 0,
        right: 0,
        bottom: 0,
        width: 0,
        height: 0
      };
    }

    this.rectChangedCallback(this.rect);
  }

  rectChangedCallback() {
    // Abstract to be implemented
  }

  getBoundingClientRect() {
    return this.rect;
  }

  get clientWidth() {
    return this.rect.width;
  }

  get clientHeight() {
    return this.rect.height;
  }
}

const pop = document.getElementById("pop");
const rangeRef = new RangeRef();
const popper = new Popper(rangeRef, pop, {
  placement: "top",
  modifiers: { offset: { offset: "0,5" } },
});

rangeRef.rectChangedCallback = ({ width }) => {
  if (width > 0) {
    popper.scheduleUpdate();
    pop.firstElementChild.classList.add('popper--visible');
  } else {
    pop.firstElementChild.classList.remove('popper--visible');
  }
};

              
            
!
999px

Console