• Stars
    star
    2,032
  • Rank 22,775 (Top 0.5 %)
  • Language
    JavaScript
  • License
    MIT License
  • Created almost 9 years ago
  • Updated 5 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

ES6 Native @mentions

Tribute

CDNJS version Build Status

A cross-browser @mention engine written in ES6, no dependencies. Tested in Firefox, Chrome, iOS Safari, Safari, IE 9+, Edge 12+, Android 4+, and Windows Phone.

Installing

There are a few ways to install Tribute; Bower, as an NPM Module, or by downloading from the dist folder in this repo.

NPM Module

You can install Tribute by running:

npm install tributejs

Or by adding Tribute to your package.json file.

Import into your ES6 code.

import Tribute from "tributejs";

Ruby Gem

To use Tribute within a Rails project, you can add the following to the app's Gemfile:

gem 'tribute'

Then, add the following to app/assets/javascripts/application.js:

*= require tribute

And in app/assets/stylesheets/application.css:

//= require tribute

Webpack

To add Tribute to your webpack build process, start by adding it to your package.json and running npm install.

After installing, you need to update your Babel module loader to not exclude Tribute from being compiled by Webpack:

{
    test: /\.js$/,
    loader: 'babel',
    exclude: /node_modules\/(?!tributejs)/
}

Download or Clone

Or you can download the repo or clone it localy with this command:

git clone [email protected]:zurb/tribute.git

You can then copy the files in the dist directory to your project.

<link rel="stylesheet" href="js/tribute.css" />
<script src="js/tribute.js"></script>

That's it! Now you are ready to initialize Tribute.

Initializing

There are two ways to initialize Tribute, by passing an array of "collections" or by passing one collection object.

var tribute = new Tribute({
  values: [
    { key: "Phil Heartman", value: "pheartman" },
    { key: "Gordon Ramsey", value: "gramsey" }
  ]
});

You can pass multiple collections on initialization by passing in an array of collection objects to collection.

var tribute = new Tribute({
  collection: []
});

Attaching to elements

Once initialized, Tribute can be attached to an input, textarea, or an element that supports contenteditable.

<div id="caaanDo">I'm Mr. Meeseeks, look at me!</div>

<div class="mentionable">Some text here.</div>
<div class="mentionable">Some more text over here.</div>

<script>
  tribute.attach(document.getElementById("caaanDo"));

  // also works with NodeList
  tribute.attach(document.querySelectorAll(".mentionable"));
</script>

A Collection

Collections are configuration objects for Tribute, you can have multiple for each instance. This is useful for scenarios where you may want to match multiple trigger keys, such as @ for users and # for projects.

Collection object shown with defaults:

{
  // symbol or string that starts the lookup
  trigger: '@',

  // element to target for @mentions
  iframe: null,

  // class added in the flyout menu for active item
  selectClass: 'highlight',

  // class added to the menu container
  containerClass: 'tribute-container',

  // class added to each list item
  itemClass: '',

  // function called on select that returns the content to insert
  selectTemplate: function (item) {
    return '@' + item.original.value;
  },

  // template for displaying item in menu
  menuItemTemplate: function (item) {
    return item.string;
  },

  // template for when no match is found (optional),
  // If no template is provided, menu is hidden.
  noMatchTemplate: null,

  // specify an alternative parent container for the menu
  // container must be a positioned element for the menu to appear correctly ie. `position: relative;`
  // default container is the body
  menuContainer: document.body,

  // column to search against in the object (accepts function or string)
  lookup: 'key',

  // column that contains the content to insert by default
  fillAttr: 'value',

  // REQUIRED: array of objects to match or a function that returns data (see 'Loading remote data' for an example)
  values: [],

  // When your values function is async, an optional loading template to show
  loadingItemTemplate: null,

  // specify whether a space is required before the trigger string
  requireLeadingSpace: true,

  // specify whether a space is allowed in the middle of mentions
  allowSpaces: false,

  // optionally specify a custom suffix for the replace text
  // (defaults to empty space if undefined)
  replaceTextSuffix: '\n',

  // specify whether the menu should be positioned.  Set to false and use in conjuction with menuContainer to create an inline menu
  // (defaults to true)
  positionMenu: true,

  // when the spacebar is hit, select the current match
  spaceSelectsMatch: false,

  // turn tribute into an autocomplete
  autocompleteMode: false,

  // Customize the elements used to wrap matched strings within the results list
  // defaults to <span></span> if undefined
  searchOpts: {
    pre: '<span>',
    post: '</span>',
    skip: false // true will skip local search, useful if doing server-side search
  },

  // Limits the number of items in the menu
  menuItemLimit: 25,

  // specify the minimum number of characters that must be typed before menu appears
  menuShowMinLength: 0
}

Dynamic lookup column

The lookup column can also be passed a function to construct a string to query against. This is useful if your payload has multiple attributes that you would like to query against but you can't modify the payload returned from the server to include a concatenated lookup column.

{
  lookup: function (person, mentionText) {
    return person.name + person.email;
  }
}

Template Item

Both the selectTemplate and the menuItemTemplate have access to the item object. This is a meta object containing the matched object from your values collection, wrapped in a search result.

{
  index: 0;
  original: {
  } // your original object from values array
  score: 5;
  string: "<span>J</span><span>o</span>rdan Hum<span>p</span>hreys";
}

Trigger tribute programmatically

Tribute can be manually triggered by calling an instances showMenuForCollection method. This is great for trigging tribute on an input by clicking an anchor or button element.

<a id="activateInput">@mention</a>

Then you can bind a mousedown event to the anchor and call showMenuForCollection.

activateLink.addEventListener("mousedown", function(e) {
  e.preventDefault();
  var input = document.getElementById("test");

  tribute.showMenuForCollection(input);
});

Note that showMenuForCollection has an optional second parameter called collectionIndex that defaults to 0. This allows you to specify which collection you want to trigger with the first index starting at 0.

For example, if you want to trigger the second collection you would use the following snippet: tribute.showMenuForCollection(input, 1);

Events

Replaced

You can bind to the tribute-replaced event to know when we have updated your targeted Tribute element.

If your element has an ID of myElement:

document
  .getElementById("myElement")
  .addEventListener("tribute-replaced", function(e) {
    console.log(
      "Original event that triggered text replacement:",
      e.detail.event
    );
    console.log("Matched item:", e.detail.item);
  });

No Match

You can bind to the tribute-no-match event to know when no match is found in your collection.

If your element has an ID of myElement:

document
  .getElementById("myElement")
  .addEventListener("tribute-no-match", function(e) {
    console.log("No match found!");
  });

Active State Detection

You can bind to the tribute-active-true or tribute-active-false events to detect when the menu is open or closed respectively.

document
  .getElementById("myElement")
  .addEventListener("tribute-active-true", function(e) {
    console.log("Menu opened!");
  });
document
  .getElementById("myElement")
  .addEventListener("tribute-active-false", function(e) {
    console.log("Menu closed!");
  });

Tips

Some useful approaches to common roadblocks when implementing @mentions.

Updating a collection with new data

You can update an instance of Tribute on the fly. If you have new data you want to insert into the current active collection you can access the collection values array directly:

tribute.appendCurrent([
  { name: "Howard Johnson", occupation: "Panda Wrangler", age: 27 },
  { name: "Fluffy Croutons", occupation: "Crouton Fluffer", age: 32 }
]);

This would update the first configuration object in the collection array with new values. You can access and update any attribute on the collection in this way.

You can also append new values to an arbitrary collection by passing an index to append.

tribute.append(2, [
  { name: "Howard Johnson", occupation: "Panda Wrangler", age: 27 },
  { name: "Fluffy Croutons", occupation: "Crouton Fluffer", age: 32 }
]);

This will append the new values to the third collection.

Programmatically detecting an active Tribute dropdown

If you need to know when Tribute is active you can access the isActive property of an instance.

if (tribute.isActive) {
  console.log("Somebody is being mentioned!");
} else {
  console.log("Who's this guy talking to?");
}

If you want to embed a link in your selectTemplate then you need to make sure that the anchor is wrapped in an element with contenteditable="false". This makes the anchor clickable and fixes issues with matches being modifiable.

var tribute = new Tribute({
  values: [
    {
      key: "Jordan Humphreys",
      value: "Jordan Humphreys",
      email: "[email protected]"
    },
    {
      key: "Sir Walter Riley",
      value: "Sir Walter Riley",
      email: "[email protected]"
    }
  ],
  selectTemplate: function(item) {
    return (
      '<span contenteditable="false"><a href="http://zurb.com" target="_blank" title="' +
      item.original.email +
      '">' +
      item.original.value +
      "</a></span>"
    );
  }
});

How do I add an image to the items in the list?

You can override the default menuItemTemplate with your own output on initialization. This allows you to replace the innerHTML of the li of each item in the list. You can use item.string to return the markup for the fuzzy match.

{
  //..other config options
  menuItemTemplate: function (item) {
    return '<img src="'+item.original.avatar_url + '">' + item.string;
  }
}

Embedding Tribute in a scrollable container.

Sometimes you may need to have the Tribute menu attach to a scrollable parent element so that if the user scrolls the container the menu will scroll with it. To do this, you can set menuContainer to the node that is the scrollable parent.

{
  //..other config options
  menuContainer: document.getElementById("wrapper");
}

Loading remote data

If your data set is large or would like to pre filter your data you can load dynamically by setting the values to a function.

{
  //..other config options
  // function retrieving an array of objects
  values: function (text, cb) {
    remoteSearch(text, users => cb(users));
  },
  lookup: 'name',
  fillAttr: 'name'
}

You would then define a function, in this case remoteSearch, that returns your data from the backend.

function remoteSearch(text, cb) {
  var URL = "YOUR DATA ENDPOINT";
  xhr = new XMLHttpRequest();
  xhr.onreadystatechange = function() {
    if (xhr.readyState === 4) {
      if (xhr.status === 200) {
        var data = JSON.parse(xhr.responseText);
        cb(data);
      } else if (xhr.status === 403) {
        cb([]);
      }
    }
  };
  xhr.open("GET", URL + "?q=" + text, true);
  xhr.send();
}

Hide menu when no match is returned

If you want the menu to not show when no match is found, you can set your noMatchTemplate config to the following:

noMatchTemplate: function () {
  return '<span style:"visibility: hidden;"></span>';
}

Detaching Tribute instances

When you want to remove Tribute from an element you can call detach.

tribute.detach(document.getElementById("caaanDo"));

This will remove all event listeners from the DOM that are associated with that element.

Trigger on multiple character strings

It is also possible to configure Tribute to trigger on a string consisting of multiple characters.

This example shows the usage of Tribute for autocompletion of variables:

var tribute = new Tribute({
  trigger: "{{",
  values: [
    { key: "red", value: "#FF0000" },
    { key: "green", value: "#00FF00" }
  ],
  selectTemplate: function(item) {
    return "{{" + item.original.key + "}}";
  },
  menuItemTemplate: function(item) {
    return item.original.key + " = " + item.original.value;
  }
});

Framework Support

Vue.js โ€” vue-tribute by @syropian

AngularJS 1.5+ โ€” angular-tribute by ZURB

Angular 2+ - ngx-tribute by Ladder.io

Ruby โ€” tribute-rb by ZURB

Ember โ€“ ember-tribute by MalayaliRobz

WYSIWYG Editor Support

Brought to you by

ZURB, the creators of Helio

Design successful products by rapidly revealing key user behaviors. Helio makes it easy to get reactions on your designs quickly so your team can focus on solving the right problems, right now.

More Repositories

1

foundation-apps

The first front-end framework created for developing fully responsive web apps.
CSS
1,580
star
2

joyride

jQuery feature tour plugin.
JavaScript
1,420
star
3

twentytwenty

jQuery Plugin to Compare Images
CSS
1,264
star
4

reveal

JavaScript
676
star
5

foundation-5-sublime-snippets

ZURB Foundation 5 Sublime Text 2 Snippets
492
star
6

orbit

JavaScript
457
star
7

responsive-tables

Tables that work responsively on small devices.
CSS
431
star
8

foundation-icons

CSS
427
star
9

pizza

Better pie, donut, line, and bar graphs.
JavaScript
413
star
10

flickrbomb

flickrBomb provides an easy way for you to fill your prototypes with relevant content, and not just those dull gray placeholder images.
JavaScript
270
star
11

foundation-icon-fonts

Foundation Icon Fonts
HTML
111
star
12

html5_video_voting

78
star
13

foundation-apps-template

Our starter template for Foundation for Apps projects. Powered by Angular, Sass, and Gulp.
CSS
74
star
14

foundation-layouts

Sample layouts for the Zurb Foundation Framework
JavaScript
54
star
15

reactive-listener

JavaScript
42
star
16

fastclick

JavaScript
34
star
17

advanced-foundation-course

Content for Advance Foundation Course
CSS
33
star
18

wrangle

JavaScript
32
star
19

foundation-course

Material for Foundation Course JavaScript
CSS
30
star
20

foundation-templates-layer

Templates that are integrated into Foundation's templates layer downloads.
JavaScript
24
star
21

css-flip-book

CSS
24
star
22

ZURB-code-standards

Code standards for ZURB's family of products and client deliverables.
19
star
23

foundation-migrate

Foundation migration plugin: http://foundation.zurb.com/docs/upgrading.html
JavaScript
16
star
24

foundation-sites-6

[ARCHIVED] Version 6.0 of Foundation for Sites (Public Beta).
HTML
14
star
25

inky-cli

Command-line interface for the Inky templating language.
JavaScript
13
star
26

angular-tribute

Angular directive for Tribute.js
JavaScript
10
star
27

front-router

Generate AngularJS states from Front Matter in state templates. Used by Foundation for Apps.
JavaScript
8
star
28

ARCHIVED-giving-tree-wordpress

7
star
29

notable-cli

Upload to Notable from the command-line
Go
7
star
30

media-query-examples

Media Query Examples
CSS
7
star
31

foundation-demo

This is a demo repository for meetups and conferences to show the process of building a simple page using Foundation.
CSS
7
star
32

foundation-inline-svg

JavaScript
7
star
33

tribute-rb

Tribute.js @mentions for the Rails Asset Pipeline
Ruby
7
star
34

foundation-compass-stack

This is a stack for developing your website using Foundation, with Assemble for templates and Compass for Sass compilation.
CSS
7
star
35

siphon-media-query

Extract media query-specific rules from CSS.
JavaScript
6
star
36

sample-scss-project

Sample Sass Project
5
star
37

F5-accessibility-audit

5
star
38

ARCHIVED-giving-tree-static

Giving Tree static site skeleton
PHP
5
star
39

topbar

topbar
CSS
4
star
40

inky-rails-talk

Live code/demo for inky-rb rails integration
Ruby
4
star
41

cardpack

A collection of Badass Foundation Flex Cards
HTML
3
star
42

zurb-template

ZURB's Client Project Template
JavaScript
3
star
43

yetinews

Tutorial project for Foundation for Apps protoype
CSS
2
star
44

10-minute-responsive-email

Code from the Foundation meetup - Foundation for Emails
JavaScript
2
star
45

wrangle-demo

Demo of wrangle.
JavaScript
2
star
46

client-template

Template for client projects, using Assemble.
CSS
1
star
47

zurb-talk-demos

CSS
1
star
48

building-blocks-ARCHIVED

HTML
1
star
49

Helio-Blog

PHP
1
star