generate botton for each information - javascript

I am pulling some information from an external database using javascript. However, I am not sure how to generate a like unlike toggle button for users to click below each information.
I place a toggle link within the appendeddata variable just to test if it would work. The link did show up below each data but the toggle effect did not work. I place the code within the same script and in its own script however no luck.
I want to also id the user that clicks the link. how can I do this?
Thank you.
here is my javascript code.
var appendedhtml = "";
var address = "";
function getDomes() {
$.ajax({
type: "GET",
url: "https://api.google.com/d/domes/explore?
ll="+lat+","+lng+"&client_id= client_id&query="+$("#query").val()+"",
success: function(data) {
$("#domes").show();
var dataobj = data.response.groups[0].items;
$("#domes").html("");
$.each( dataobj, function() {
if (this.dome.categories[0]) {
str = this.dome.categories[0].icon.prefix;
newstr = str.substring(0, str.length - 1);
icon = newstr+this.dome.categories
[0].icon.suffix;
} else {
icon = "";
}
if (this.dome.location.address) {
address = '<p
class="subinfo">'+this.dome.location.address+'<br>';
} else {
address = "";
}
appendedtml = '<div
class="venue"><h2><span>'+address'</span></h2> <hr>';
$("#domes").append(appendedhtml);
});
}
});
}
I place the code that would give the toggle effect within the main script and by itself
with no luck.
<script>
$(document).ready(function(){
$('a.toggler').click(function(){
$(this).toggleClass('off');
});
});
this is the css for the toggle button. I know the button works indecently just cannot get it
to work with the data output from the main script.
a.toggler {
background: green;
cursor: pointer;
border: 2px solid black;
border-right-width: 15px;
padding: 0 5px;
border-radius: 5px;
text-decoration: none;
transition: all .5s ease;
}
a.toggler.off {
background: red;
border-right-width: 2px;
border-left-width: 15px;
}

try jQuery's on method instead of click method.
$( "a.toggler" ).on( "click", function() {
$(this).toggleClass('off');
});
on method listens to events from elements that were created dynamically.

Related

Is ther a way to use JS to automaticly populate nav-links?

So I am still fairly early in my BED-Studies, and would apreciate any sugestions to be more code hevy than smart and eficient, this way i can learn.
Case:
I am creating a website that displays how the studies evolves my skills. And like any other page, it needs a nav-bar but it can get a little time consuming to retype every relevant link.
I have tried to google the question, but as far as i can find ther is a resounding "nope!", but then again, somthimes i com across people using a "file" function but i cant seem to find this function in jquery or js.
And thus back to my question: is ther a way to make JavaScript "see" the files in a location without hard-coding them in?
my folder structure is likethis:
/school:
./index.html
./css
./js
./programingFoundation:
./pfdindex.html
./module1
./module2
./...
./frontEndTech:
./fetIndex.html
./module1
./module2
,/..
Edit:
my attempt at navigation (mainNav.js):
function innsertMainNav() {
const mainNavTitles = [
{ button: 'Home', url: '/index.html', active: true },
{ button: 'JS Basic', url: '/programmingFoundations/pfindex.html', active: true },
{ button: 'Frontend', url: '/forntEndTechnologies/fetindex.html', active: true },
{ button: 'OOP', url: './programmingWithObjects/pwoindex.html', active: true },
{ button: 'Project Methodology', url: '/projectMethodology/pmindex.html', active: false },
{ button: 'Semester Project', url: '/semesterProject/smindex.html', active: false },
{ button: 'JS Servers', url: '/javaScriptServers/jssindex.html', active: false },
{ button: 'dataBases', url: '/databases/dbindex.html', active: false },
{ button: 'Rest Api', url: '/restApi/raindex.html', active: false },
{ button: 'Servers', url: '/serverDeployment/sdindex.html', active: false },
{ button: 'Exam', url: '/examProject/epindex.html', active: false }
];
//insert header
$('head').after('<header class="container-md bg-light"></header>');
// add title and logo line
$('header').append(`<div class="row"><h1>${document.title}</h1></div>`);
// add Navigation
$('header').append('<nav id="mainNav" class="row">\
<div id="outerDiv" class="container-fluid">\
<button\
class="navbar-toggler" type="button" data-bs-toggle="collapse"\
data-bs-target="#collapsing" aria-controls="collapsing"\
aria-expanded="false" aria-label="Togglenavigation">\
<span class="navbar-toggler-icon"></span>\
</button>\
<div id="collapsing" class="collapse navbar-collapse">\
<ul id="mainNavList" class="navbar-nav me-auto mb-2 mb-lg-0"></ul>\
</div></div></nav>');
// Populate main NAV
for (let i = 0; i < 10; i++) {
if (mainNavTitles[i].active) {
$('ul#mainNavList').append(`<li>\
<a class="col" href="..${mainNavTitles[i].url}">\
${mainNavTitles[i].button}\
</a></li>`);
}
else {
break;
}
}
$('#mainNav').addClass('navbar navbar-expand-lg navbar-light');
$('nav a').addClass('nav-link');
}
If you're only using HTML and vanilla JS, you could do it like this.
First, create a javascript file, and include it in every main html file you use on your website (for example, index.html. contact.html, portfolio.html, etc).
Let's call this file helpers.js.
Its contents would be something like this.
// Let's see if the HTML which is using helpers.js has its own navigation
var navigation = document.querySelectorAll("nav");
if(!navigation.length) {
var navigation = document.createElement("nav");
} else {
navigation = navigation[0];
}
// We'll make a centralized list of main links - the ones we want to see in the navigation
var linkList = {
"home":"index.html",
"portfolio":"portfolio.html",
"about me":"about.html",
"contact me":"contact.html"
};
// The function is here to help us populate the navigation with our links
function fillNavigation(elem, source) {
elem.innerHTML = "";
for(let description in source) {
let content = '' + description + '';
elem.innerHTML += content;
}
}
// We call the function, and let it work for us
fillNavigation(navigation,linkList);
One advantage of this is that you have one place where you change your main links list - you wouldn't have to visit each and every page of your site in order to make changes to your navigation. The helpers.js will do this for you.
A running example (JS fiddle here, in case you want to play around with this)
var navigation = document.querySelectorAll("nav");
if(!navigation.length) {
var navigation = document.createElement("nav");
} else {
navigation = navigation[0];
}
var linkList = {
"home":"index.html",
"portfolio":"portfolio.html",
"about me":"about.html",
"contact me":"contact.html"
};
function fillNavigation(elem, source) {
elem.innerHTML = "";
for(let description in source) {
let content = '' + description + '';
elem.innerHTML += content;
}
}
// The timeout is here for illustrative purposes only
setTimeout(function() {
fillNavigation(navigation,linkList);
},2000);
nav {
padding: 5px;
width: calc(100% - 10px);
text-align: center;
background-color: #333;
color: #fff;
}
nav a {
text-decoration: none;
padding: 3px;
}
nav a:hover {
color: red;
transition: 0.5s;
}
<nav>ok</nav>
<p>
lorem ipsum
sit dolor amet
etc lupus in fabula
homo homini lupus est
similis simili gaudet
bellum omnia contra omni
</p>
<hr>
<p>
some other stuff
</p>
EDIT For cases where you have multi-level menus, you could use something like this. As in the first example, a fiddle is available here
var navigation = document.querySelectorAll("nav");
if(!navigation.length) {
var navigation = document.createElement("nav");
} else {
navigation = navigation[0];
}
var linkList = {
"home":"index.html",
"portfolio":"portfolio.html",
"about me":"about.html",
"contact me":"contact.html",
"modules":{
"module1": {
"lorem":"lorem.html",
"ipsum":"ipsum.html"
},
"module2": {
"foo":"foo.html",
"bar":"bar.html"
}
}
};
function isObject(obj) {
return typeof obj === 'object';
}
function renderLinks(source,flagClose) {
let txt = "";
for(let description in source) {
if(isObject(source[description])) {
txt += '<li class="parent">' + description + '<ul class="sublevel none">';
txt += renderLinks(source[description],description);
} else {
let addition = flagClose ? (flagClose.trim() + '/') : ''
txt += '<li>' + description + '</li>';
}
}
if(flagClose) {
txt += '</ul></li>';
}
return txt;
}
function fillNavigation(elem, source) {
elem.innerHTML = "";
elem.innerHTML += renderLinks(source);
// show on hover
$("li.parent").mouseenter(function() {
$(this).find("> ul.sublevel").removeClass("none");
});
$("li.parent").mouseleave(function() {
$(this).find("> ul.sublevel").addClass("none");
});
}
fillNavigation(navigation,linkList);
.none {
display: none !important;
}
nav {
padding: 5px;
width: calc(100% - 10px);
text-align: left;
background-color: #333;
color: #fff;
}
nav a {
text-decoration: none;
padding: 3px;
}
nav a:hover {
color: red;
transition: 0.5s;
}
li {
list-style: none;
}
li.parent {
position: relative;
padding-right: 10px;
padding-left: 5px;
}
li.parent * {
display: block;
}
ul.sublevel {
position: absolute;
top: -1px; /* because of the border */
left: 100%;
background-color: #333;
margin-top: 0px;
margin-bottom: 0px;
padding-left: 10px;
min-width: 100px;
border: 1px solid #aaa;
text-align: left;
}
li.parent, ul.sublevel {
cursor: pointer;
}
nav li {
display: inline-block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<nav>ok</nav>
<p>lorem ipsum sit dolor amet etc lupus in fabula homo homini lupus est similis simili gaudet bellum omnia contra omni </p>
<hr>
<p>some other stuff</p>
You will notice a few differences from the first case (one-level menu):
the original linkList has been modified, in order to show the case when there's a multi-level menu (modules/module1/link1.html, modules/module1/link2.html, etc)
the function fillNavigation has been changed, as well - it now contains a call to a new function, renderLinks, which creates all the innerHTML
renderLinks is a recursive function, which makes it handy for cases where you decide to change the depth of your navigation. Please note that it has been tested on one and two-level menus. It should work for more levels than that, but you would have to adjust the CSS accordingly (or change the function a bit, so that you may have specific CSS classes, connected with a specific level)
the function renderLinks relies on a helper function, isObject. The purpose of this function is to test whether the item we're currently processing is actually a sub-menu. Since we control the original linkList and its structure, we can do it as shown in the code. For more complex cases (for example, where we have no, or little control on how the link list is formed, this solution would most likely be lacking)
hover effect and showing of sub-level menus is handled via jQuery. There is no special reason for this, other than that I found it simpler to achieve (less to type)
since we can have more than one menu element with children, mouseenter and mouseleave target only the first sub-level within the one we're hovering on. This also lets us travel (visually) along the hovered route, without closing the original top-level menu item which contains the one's we're currently traversing
The question here seems to be, how do I read a local filesystem from a web browser. Browsers have a FileAPI but it does not offer the ability to pass a list of the files and folders on the local filesystem without user input.

Display a message once (like a cookie consent) on first visit

I'm building a website and am trying to display a message at the top of the page (inside the header) so it only appears once on every visit/session. An example of this the 'Book an appointment' green bar at the top of this website:
https://www.tiffany.co.uk
My website is here: https://vitrify.tempurl.host/
I've got as far as having a message appear (orange panel at top of page) but currently it appears every time a page is loaded. I just want it to appear once, just like a cookie consent.
I've spent hours looking for a solution but, as I'm not a programmer, I'm struggling. Any help would be much appreciated.
Here's the HTML:
function myFunction() {
var x = document.getElementById("topDIV");
if (x.style.display === "none") {
x.style.display = "block";
} else {
x.style.display = "none";
}
}
.topDIV {
color: #000000;
font-weight: 300;
font-size: 15px;
}
.topDIV a:link, .topDIV a:visited {
color: #000000!important;
font-weight: 500;
letter-spacing: -0.3px;
line-height: 1.2 ;
}
span.topDIV {
}
.topDIV a:hover {
border-bottom: 1px solid rgba(0,0,0,0.50) !important;
display: inline-block;
}
.button-x {
position: relative;
float: right;
top: -5px;
background:none;
border:none;
color:rgb(0,0,0) ;
cursor: pointer;
vertical-align: 0px;
}
.button-x:before {
font-family: 'Times';
content: "X";
font-size:30px;
vertical-align:0px;
opacity:0.5;
}
.button-x:hover {
opacity:1!important;
}
<span class = "topDIV">Welcome to <em>Vitrify</em>. Following in the finest traditions of vitreous enamelled jewellery. Find out more.</span><button class = "button-x" onclick="myFunction()"></button>
As mentioned above you need to use localStorage to solve your problem.
Need to know when page is loaded
Get value from the localStorage
Add event to the button (i removed the onclick event from the html for a cleaner solution)
After click set value for localStorage and hide item
If localStorage have value, hide the element
working example
Javascript
document.addEventListener('DOMContentLoaded', function () {
const topDiv = document.querySelector('.topDIV');
const buttonX = document.querySelector('.button-x');
// Get value from localStorage when open the page
const lS = localStorage.getItem('first-visit');
// Add button 'click' event
buttonX.addEventListener('click', () => {
// Set value to the localStorage
localStorage.setItem('first-visit', false);
// hide DOM element
topDiv.style.display = 'none';
});
// This does not check on the first visit to the page
// If localStorage have value, hide DOM element
if (lS) topDiv.style.display = 'none';
});
Html
<span class="topDIV"
>Welcome to <em>Vitrify</em>.Following in the finest traditions of
vitreous enamelled jewellery.
Find out more.
</span>
<button class="button-x"></button>
You can add a flag "showTopMessage:true" in your local storage or session storage based on one time msg to user or every time he visits. respectively.
On Cross/close icon click set the flag "showTopMessage:false".
const showTopBar = "SHOW_TOP_BAR";
const[showTopBar,setShowTopBar] = useState(true);
useEffect(()=>{
const saveState = localstorage.getItems(showTopBar);
if(saveState) setShowTopBar(saveState);
else localstorage.setItem(showTopBar, true);
},[]);
const handleTopBarClose = () => {
setShowTopBar(false);
localstorage.setItem(showTopBar, false);
}
return(
<div>
{
showTopBar && <TopBarComponent onClose={handleTopBarClose}/>
}
</div>
)

Drag and drop file to specific target

I have a set of elements of media files listed like this.
I want to upload a a media file from windows explorer to a specific target of this list(for eg- It should be possible to add file above 'Title A' or below 'Title A'... ). How to achieve this?I know how to upload a file and it is achievable for me if it's to add to the end of the list. What I don't get is how to get that specific position which I need to upload.
(If it's a DOM element, I could have used jQuery draggable droppble, but again it's not possible here! :(. )
I am using ember and I have created component and added it after each element
abc: $('<div class="bla"></div>'),
didInsertElement() {
this._super(...arguments);
var self = this;
$('.xyz').bind("dragenter", function (evt) {
event.preventDefault();
event.stopPropagation();
var targetElement = $(evt.target).prev();
var holder = self.get('abc');
targetElement.after(holder);
});
$('.xyz').bind("dragleave", function (evt) {
event.preventDefault();
$('.bla').detach();
});
$('.xyz').bind("drop", function (evt) {
event.preventDefault();
$('.bla').detach();
});
},
With this I could successfully add a div below the target element. But with 'dragleave' it's executing every time and hence it's detaching everytime and also if I don't want to drop it or drop outside the window, it's not detaching at all.(something like dragend?)
.bla{
position: relative;
clear: both;
cursor: default;
margin-top: 2px;
min-height: 32px;
line-height: 32px;
overflow: hidden;
background-color: white;
border: 1px solid #999;
}
Any help?!
Thank you.

Dynamically change the background of div based on the returned value of function

Here is the function
function getColor() {
var color = '';
$('.animated-teaser.cold').hover(
function() {
$(this).addClass('active');
color = 'coldest';
return color;
},
function() {
$(this).removeClass('active');
color = '';
return color;
}
);
$('.animated-teaser.warm').hover(
function() {
$(this).addClass('active');
color = 'warmest';
return color;
},
function() {
$(this).removeClass('active');
color = '';
return color;
}
);
}
Then I call it
$('.area').css('background-color', getColor());
I see that the function it works, but the result doesn't affect the ".area" element.
I don't want to add an event to the ".area" element!
Here is jsfiddle
As I said in the comments, the getColor function attaches hovering listeners to DOM elements. That's all it does. It does not return a color. Currently, you're expecting this function to return a color from a future event that has not happened yet (the user hovering an element). The logic is flawed, you need to rethink it. First, attach the hovering listeners. Then, on hover, change the area color.
const $area = $('.area'),
$cold = $('.animated-teaser.cold'),
$warm = $('.animated-teaser.warm');
$cold.hover(function() {
$cold.addClass('active');
changeAreaColor('coldest');
}, function() {
$cold.removeClass('active');
changeAreaColor('');
});
$warm.hover(function() {
$warm.addClass('active');
changeAreaColor('warmest');
}, function() {
$warm.removeClass('active');
changeAreaColor('');
});
}
function changeAreaColor(color) {
$area.css('background-color', color)
}
This being said, "warmest" is an invalid CSS color, so it won't do anything. Change it to "crimson" or something official.
Should be tying to the hover event instead of a one time update, i.e.
$('.area.animated-teaser.cold').hover(
function() {
$(this).addClass('active');
color = 'coldest';
return color;
},
function() {
$(this).removeClass('active');
color = '';
return color;
}
);
Your JS is flawed for several reasons, not least of all due to the semantics of attaching event handlers in a function designed to return a colour value, which itself returns no value.
However, given your HTML, you don't need JS for this at all. You can do this more effectively in CSS by using the General sibling selector (~) and :hover.
Also note that coldest and warmest are not valid options for the background-color rule. You need to use a recognised colour name or value. In the following example I used lightblue and coral. Try this:
.animated-teaser {
width: 50px;
height: 50px;
display: inline-block;
}
.animated-teaser:hover {
border: 2px solid red;
}
.animated-teaser.cold {
background: blue;
}
.animated-teaser.warm {
background: orange;
}
.animated-teaser.cold:hover ~ .area {
background-color: lightblue;
}
.animated-teaser.warm:hover ~ .area {
background-color: coral;
}
.area {
display: block;
width: 200px;
height: 100px;
background: #f1f1f1;
}
<div class="animated-teaser cold"></div>
<div class="animated-teaser warm"></div>
<div class="area"></div>
Just put these 2 lines in CSS. No need of javascript for this simple task
.cold:hover ~ .area {
background: blue
}
.warm:hover ~ .area {
background: orange
}

Twitter-style autocomplete in textarea

I am looking for a Javascript autocomplete implementation which includes the following:
Can be used in a HTML textarea
Allows for typing regular text without invoking autocomplete
Detects the # character and starts autocomplete when it is typed
Loads list of options through AJAX
I believe that this is similar to what Twitter is doing when tagging in a tweet, but I can't find a nice, reusable implementation.
A solution with jQuery would be perfect.
Thanks.
Another great library which solves this problem At.js (deprecated)
Source
Demo
They are now suggesting the Tribute library
https://github.com/zurb/tribute
Example
I'm sure your problem is long since solved, but jquery-textcomplete looks like it would do the job.
Have you tried this
GITHUB: https://github.com/podio/jquery-mentions-input
DEMO/CONFIG: http://podio.github.io/jquery-mentions-input/
It is pretty simple to implement.
I've created a Meteor package for this purpose. Meteor's data model allows for fast multi-rule searching with custom rendered lists. If you're not using Meteor for your web app, (I believe) you unfortunately won't find anything this awesome for autocompletion.
Autocompleting users with #, where online users are shown in green:
In the same line, autocompleting something else with metadata and bootstrap icons:
Fork, pull, and improve:
https://github.com/mizzao/meteor-autocomplete
Try this:
(function($){
$.widget("ui.tagging", {
// default options
options: {
source: [],
maxItemDisplay: 3,
autosize: true,
animateResize: false,
animateDuration: 50
},
_create: function() {
var self = this;
this.activeSearch = false;
this.searchTerm = "";
this.beginFrom = 0;
this.wrapper = $("<div>")
.addClass("ui-tagging-wrap");
this.highlight = $("<div></div>");
this.highlightWrapper = $("<span></span>")
.addClass("ui-corner-all");
this.highlightContainer = $("<div>")
.addClass("ui-tagging-highlight")
.append(this.highlight);
this.meta = $("<input>")
.attr("type", "hidden")
.addClass("ui-tagging-meta");
this.container = $("<div></div>")
.width(this.element.width())
.insertBefore(this.element)
.addClass("ui-tagging")
.append(
this.highlightContainer,
this.element.wrap(this.wrapper).parent(),
this.meta
);
var initialHeight = this.element.height();
this.element.height(this.element.css('lineHeight'));
this.element.keypress(function(e) {
// activate on #
if (e.which == 64 && !self.activeSearch) {
self.activeSearch = true;
self.beginFrom = e.target.selectionStart + 1;
}
// deactivate on space
if (e.which == 32 && self.activeSearch) {
self.activeSearch = false;
}
}).bind("expand keyup keydown change", function(e) {
var cur = self.highlight.find("span"),
val = self.element.val(),
prevHeight = self.element.height(),
rowHeight = self.element.css('lineHeight'),
newHeight = 0;
cur.each(function(i) {
var s = $(this);
val = val.replace(s.text(), $("<div>").append(s).html());
});
self.highlight.html(val);
newHeight = self.element.height(rowHeight)[0].scrollHeight;
self.element.height(prevHeight);
if (newHeight < initialHeight) {
newHeight = initialHeight;
}
if (!$.browser.mozilla) {
if (self.element.css('paddingBottom') || self.element.css('paddingTop')) {
var padInt =
parseInt(self.element.css('paddingBottom').replace('px', '')) +
parseInt(self.element.css('paddingTop').replace('px', ''));
newHeight -= padInt;
}
}
self.options.animateResize ?
self.element.stop(true, true).animate({
height: newHeight
}, self.options.animateDuration) :
self.element.height(newHeight);
var widget = self.element.autocomplete("widget");
widget.position({
my: "left top",
at: "left bottom",
of: self.container
}).width(self.container.width()-4);
}).autocomplete({
minLength: 0,
delay: 0,
maxDisplay: this.options.maxItemDisplay,
open: function(event, ui) {
var widget = $(this).autocomplete("widget");
widget.position({
my: "left top",
at: "left bottom",
of: self.container
}).width(self.container.width()-4);
},
source: function(request, response) {
if (self.activeSearch) {
self.searchTerm = request.term.substring(self.beginFrom);
if (request.term.substring(self.beginFrom - 1, self.beginFrom) != "#") {
self.activeSearch = false;
self.beginFrom = 0;
self.searchTerm = "";
}
if (self.searchTerm != "") {
if ($.type(self.options.source) == "function") {
self.options.source(request, response);
} else {
var re = new RegExp("^" + escape(self.searchTerm) + ".+", "i");
var matches = [];
$.each(self.options.source, function() {
if (this.label.match(re)) {
matches.push(this);
}
});
response(matches);
}
}
}
},
focus: function() {
// prevent value inserted on focus
return false;
},
select: function(event, ui) {
self.activeSearch = false;
//console.log("#"+searchTerm, ui.item.label);
this.value = this.value.replace("#" + self.searchTerm, ui.item.label) + ' ';
self.highlight.html(
self.highlight.html()
.replace("#" + self.searchTerm,
$("<div>").append(
self.highlightWrapper
.text(ui.item.label)
.clone()
).html()+' ')
);
self.meta.val((self.meta.val() + " #[" + ui.item.value + ":]").trim());
return false;
}
});
}
});
body, html {
font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
}
.ui-tagging {
position: relative;
border: 1px solid #B4BBCD;
height: auto;
}
.ui-tagging .ui-tagging-highlight {
position: absolute;
padding: 5px;
overflow: hidden;
}
.ui-tagging .ui-tagging-highlight div {
color: transparent;
font-size: 13px;
line-height: 18px;
white-space: pre-wrap;
}
.ui-tagging .ui-tagging-wrap {
position: relative;
padding: 5px;
overflow: hidden;
zoom: 1;
border: 0;
}
.ui-tagging div > span {
background-color: #D8DFEA;
font-weight: normal !important;
}
.ui-tagging textarea {
display: block;
font-family: "lucida grande",tahoma,verdana,arial,sans-serif;
background: transparent;
border-width: 0;
font-size: 13px;
height: 18px;
outline: none;
resize: none;
vertical-align: top;
width: 100%;
line-height: 18px;
overflow: hidden;
}
.ui-autocomplete {
font-size: 13px;
background-color: white;
border: 1px solid black;
margin-bottom: -5px;
width: 0;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<textarea></textarea>
http://jsfiddle.net/mekwall/mcWnL/52/
This link will help you
I could not find any solution that matched my requirements perfectly, so I ended up with the following:
I use the jQuery keypress() event to check for the user pressing the # character.
If this is the case, a modal dialog is shown using jQuery UI. This dialog contains an autocomplete text field (many options can be used here, but I recommmend jQuery Tokeninput)
When the user selects an option in the dialog, a tag is added to the text field and the dialog is closed.
This is not the most elegant solution, but it works and it does not require extra keypresses compared to my original design.
Edit
So basically, we have our large text box where the user can enter text. He should be able to "tag" a user (this just means inserting #<userid> in the text). I attach to the jQuery keyup event and detect the # character using (e.which == 64) to show a modal with a text field for selecting the users to tag.
The meat of the solution is simply this modal dialog with a jQuery Tokeninput text box. As the user types here, the list of users is loaded through AJAX. See the examples on the website for how to use it properly. When the user closes the dialog, I insert the selected IDs into the large text box.
Recently i had to face this problem and this is how i nailed down...
Get the string index at the cursor position in the textarea by using selectionStart
slice the string from index 0 to the cursor position
Insert it into a span (since span has multiple border boxes)
Get the dimensions of the border box using element.getClientRects() relative to the view port. (here is the MDN Reference)
Calculate the top and left and feed it to the dropdown
This works in all latest browsers. haven't tested at old ones
Here is Working bin
Another plugin which provides similar functionality:
AutoSuggest
You can use it with custom triggers or you can use it without any triggers. Works with input fields, textareas and contenteditables. And jQuery is not a dependency.
I would recommend the textcomplete plugin. No jQuery dependency. You may need bootstrap.css to refer, but I recommend to write your own CSS, lighter and simple.
Follow the below steps to give it a try
npm install #textcomplete/core #textcomplete/textarea
Bind it to your input element
const editor = new TextareaEditor(inputEl);
const textcomplete = new Textcomplete(editor, strategy, options);
Set strategy(how to fetch suggestion list) and options(settings to configure the suggestions) according to your need.
JS version
Angular Version
This small extension seems to be the closest at least in presentation to what was asked. Since it's small, it can be easily understood and modified. http://lookapanda.github.io/jquery-hashtags/
THIS should work. With regards to the # kicking off the search, just add (dynamically or not) the symbol to the beginning of each possible search term.

Categories

Resources