Creating a suggestion box within an HTML container using JavaScript

https://i.sstatic.net/Asi5r.pngI am currently working on a page where users can click on a word and receive a small suggestion box (like a tiny popup) to choose synonyms they prefer.

Although I believe this can be achieved with JavaScript, I haven't come across any clear examples of how to implement it.

The HTML code snippet is provided below:

Original:
I <b class="synonyms" style="color:black;" 
title="love|really like|really love">like</b> apples.

The desired result should look like this (after a user selects a synonym):
I <b>{like|love}</b> apples.

For instance, when the user clicks on "like" in the sentence "I like apples," a small suggestion box should appear with various options to choose from (such as love, really like, really love).

The final output would include the original text along with the user's chosen synonym.

This example showcases how it can be implemented in JavaScript. However, I'm unsure if it's possible to select specific words (since there could be multiple words in a sentence) and customize the appearance of the suggestion box by adding a list of clickable words for selection.

<!DOCTYPE html>
<html>
<body>

<p>I <b id="demo">like</b> apples.</p>

<button onclick="choose()">Try it</button>


<script>
function choose() {
    var synonym = prompt("Choose synonyms:", "like");
    
    if (synonym != null) {
        document.getElementById("demo").innerHTML =
        "{" + "like" + "|" + synonym + "}";
    }
}
</script>

</body>
</html>

Answer №1

Although I'm uncertain about the ability to click on specific words (especially when dealing with multiple words in a sentence) and if there's a way to customize the suggestion box appearance and include a list of selectable words.

To tackle your problem effectively, it's advisable to break it down into smaller steps. By doing so, you can grasp the problem domain better and devise a solution. Based on your query, here are some high-level steps along with their potential implementations:

Note 1: This solution is solely JavaScript-based. Keep in mind that frameworks like jQuery are essentially abstractions of JavaScript at a higher level. It's crucial to have a solid understanding of basic JavaScript before delving deeper.

Note 2: Throughout this response, important concepts are hyperlinked for further information and learning purposes.

1) Setting up Markup and JavaScript for Words: Identify words with synonyms present. These synonyms should be marked within the title attribute in the HTML markup as illustrated below:

Markup:

<p>
    I <i class="synonyms" title="love|really like|really love">like</i> apples. 
    I <i class="synonyms" title="love|relish|savor|enjoy|patronize|adore">like</i> oranges. 
</p>

To target all words carrying synonyms using JavaScript, create an array of these elements by querying the DOM:

JavaScript:

var words = [].slice.call(document.querySelectorAll('i.synonyms'));

The use of querySelectorAll returns a node list which can be converted to an array via array slicing. An array structure enables easy iteration later on.

Answer №2

Check out this jQuery component I developed specifically to meet your requirements.
Feel free to view the demo on jsbin if that works better for you.

//custom jquery component
$.fn.synonyms = function(options){
  options = $.extend({}, {separator: '|'}, options);
  this.each(function(index, element){
    var $element = $(element),
        originalText = $element.text(),
        originalTextSpan = $('<span>'+originalText+'</span>');
    $element.html(originalTextSpan);
    var suggestionBox = '<div>';
    $.each($element.attr('data-synonyms').split(options.separator),
           function(key, suggestion){
      suggestionBox+='<span>'+suggestion+'</span> - ';
    }
          );
    suggestionBox = suggestionBox.slice(0, -2);
    suggestionBox += '</div>';
    suggestionBox = $(suggestionBox);
    suggestionBox.css({
      display: 'none'
    });
  
    $element.click(function(){
      suggestionBox.toggle();
    });
  
    suggestionBox.on('click','span',function(){
      var selectedText = $(this).text();
      originalTextSpan.text('{'+originalText+'|'+selectedText+'}');
      onSelected(selectedText);
    });
  
    $element.append(suggestionBox);
  });
  
  
  function onSelected(selectedText){
    if(options.onSelected){
      options.onSelected(selectedText);
    }
  }
};


// Implementation of the component
$(function(){
  $('[data-synonyms]').synonyms({
    onSelected: function(selectedText){
      alert('you selected:'+selectedText);
    }
  });
});
div[data-synonyms]{
  display: inline;
  position: relative;
  cursor: pointer;
  text-decoration: underline;
}
div[data-synonyms] > div{
  white-space: nowrap;
  position: absolute;
  top: 1.2em;
  left: 0;
  background: #fff;
  border: 1px solid #bbb;
  padding: 2px;
}
div[data-synonyms] > div > span{
  text-decoration: underline;
  cursor: pointer;
}
<!DOCTYPE html>
<html>
<head>
<script src="https://code.jquery.com/jquery-2.1.4.js"></script>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  I <div data-synonyms="love|really like|really love">like</div> apples. Carrots are the <div data-synonyms="worst|orangest">best</div> though.
</body>
</html>

Answer №3

$(document).ready(function() {
  $("b").on("click", function() {
    var $b = $(this),
      choices = this.title.split('|').join('</option><option>');
    $b.after('<select class="selector"><option>&nbsp;</option><option>' + choices + '</option><select>');
  });
  $("body").on("change", "select.selector", function() {
    var $sl = $(this),
      text = $sl.val();
    $sl.prev('b').text(text);
    $sl.remove();
  });
});
b {
  color: red;
  cursor: pointer;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

<div class="text">
  <p>I <b title="like|love|adore">like</b> apples, <b title="especially|particularily">particularly</b> if they are <b title="fresh|ripe">ripe</b>.</p>

</div>

Answer №4

Check out my new approach using bootstrap's dropdown feature:

http://www.bootply.com/rWfTgSwf1z

The concept is to utilize dropdowns for words that need synonyms. My method involves manually adding dropdowns for each word and replacing the original word upon selection.

You can structure your sentences like this, with synonyms specified in the data-synonyms attribute:

<div>
  I
  <span data-synonyms="love|really like|really love">like</span>
  apples and
  <span data-synonyms="mangoes|bananas|other fruits">oranges</span>.
</div>

In the javascript portion, we generate the dropdowns and substitute the existing elements:

$('[data-synonyms]').each(function () {

  // access the current element
  var $this = $(this);

  // create a wrapper for the dropdown
  var $dropdownDiv = $('<div>').addClass('dropdown word-dropdown');

  // establish the dropdown trigger
  var $a = $('<a>').attr('data-toggle', 'dropdown').text($this.text()).appendTo($dropdownDiv);

  // form the dropdown list
  var $ul = $('<ul>').addClass('dropdown-menu').appendTo($dropdownDiv);

  // retrieve the synonyms and include the original word
  var synonyms = $this.attr('data-synonyms').split('|');
  synonyms.splice(0, 0, $this.text());

  // generate an entry in the dropdown for each synonym
  $.each(synonyms, function (idx, syn) {
    var $li = $('<li>').addClass('synonyms').appendTo($ul).append($('<a>').text(syn.trim()));

    // incorporate a function to replace the original word with the synonym
    $li.on('click', function () {
        $a.text(syn.trim());
    });
  });

  // swap the current element with the dropdown element
  $this.replaceWith($dropdownDiv);

  // activate the dropdown functionality
  $a.dropdown();

});

Answer №5

Check Out This Fiddle

I've utilized jQuery and made some changes to the syntax. Every option is now a span with the class of selectable. Additionally, it requires an options attribute. The options are displayed in the manner you suggested.

A unique aspect of my script is that it omits listing the already selected option and provides keyboard accessibility (try using Tab, Enter, and arrow keys).

$(function () {
  $('.selectable').each(function () {
    var $this = $(this)
    var list  = $this.attr('options').split('|')
    var text  = $this.text()
    $this
      .data('original', text)
      .html('<div><span>' + text + '</span><ul><li tabindex="0">' + list.join('</li><li tabindex="0">') + '</li></ul></div>')
  }).on('mousedown', function (e) {
    e.preventDefault()
    var $this   = $(this)
    var $target = $(e.target)
    var $focus  = $this.find(':focus')
    if ($focus.length) $focus.blur()
    else $this.find('li:not(.active)').eq(0).focus()
    if ($target.is('li')) changeSelection($this, $target)
  }).on('keydown', function (e) {
    var which = e.which
    if (which === 13) changeSelection($(this))
    else if (which === 40) $(this).find(':focus').next().focus()
    else if (which === 38) $(this).find(':focus').prev().focus()
  })
  function changeSelection ($this, $target) {
    $target = $target || $this.find(':focus')
    $target.blur()
    $this
      .one('transitionend', function () {
        $target.addClass('active').siblings().removeClass('active').last().focus().blur()
      })
      .addClass('filled')
      .find('span').text($this.data('original') + ' | ' + $target.text())
  }
})
body {
  font-family: sans-serif;
}

.selectable {
  cursor: default;
}

.selectable:focus {
  outline: none;
}

.selectable div {
  position: relative;
  display: inline-block;
}

.selectable::before,
.selectable::after,
.selectable span {
  color: #F00;
}

.selectable.filled::before {
  content: '{';
  margin-right: .2em;
}

.selectable.filled::after {
  content: '}';
  margin-left: .2em;
}

.selectable ul {
  position: absolute;
  top: calc(100% + .2em);
  left: calc(-.4em - 1px);
  list-style: none;
  margin: 0;
  padding: 1px 0 0;
  overflow: hidden;
  background-color: #DDD;
  z-index: 1;
}

.selectable ul:not(:focus-within) {
  pointer-events: none;
  user-select: none;
  opacity: 0;
  transform: translateY(-5px);
  transition: opacity .25s ease, transform .4s ease;
}

.selectable ul:focus-within {
  transition: opacity .25s ease, transform .4s ease -.15s;
}

.selectable li {
  white-space: nowrap;
  padding: .4em;
  margin: 0 1px 1px;
  background-color: #FFF;
}

.selectable li:hover {
  background-color: #F7F7F7;
}

.selectable li.active {
  display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<p>I <span class="selectable" options="love|really like|really love">like</span> apples.</p>
<p>There's nothing <span class="selectable" options="in this world|in the universe">on earth</span> I eat more than apples.</p>

We hope this explanation assists you!

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Confusion surrounding the purpose of an AngularJS controller function

After experimenting with some basic tutorials in AngularJS, I came across a discrepancy in how controllers are declared and used. For instance, in this JSFiddle link - http://jsfiddle.net/dakra/U3pVM/ - the controller is defined as a function name, which w ...

Utilizing CSS3 to perform multiple 3D rotations

I am attempting to rotate a face of a cube twice in order to have that face fall flat on one side. For reference, please see the illustration of my goal here: https://i.stack.imgur.com/kwO8Z.png I have included my current progress below, using a 45-deg ...

What are the limitations on cross-site scripting for local (file:///) pages?

I am currently working on an HTML/Javascript application that makes use of jQuery to communicate with a web server that I unfortunately do not have control over. The application runs smoothly when accessed locally through the file:///app.html URL on Safari ...

React snap scrolling feature is not working as intended

I've been attempting to implement the snap scroll feature in my react app, but I'm facing issues with it. The regular CSS method doesn't seem to work well in Chrome, as discussed in this Scroll Snap Skips Section on Smaller Screen - Chrome t ...

What could be causing the property of the object to be inaccessible in a ternary equation, yet functional in JSX in React?

I have an object that resembles the structure below: campground { "uri": "/campgrounds/long-island-bridge-campground-llc/", "acfDetails": { "picture": { "mediaItemUrl": "/uploads/2021/04/ ...

Create a concise shorthand representation for margins when styling with jss using Material-UI's theme object

When styling the component, I found a way to apply the margin in a specific manner. style.js const styles = theme => ({ button: { margin: '12px 18px', } }) However, I wanted to utilize material-ui's theme.spacing.unit f ...

Dealing with Failed Axios Requests in React

I'm having trouble figuring out what I'm doing incorrectly. My goal is for the Main component to pass the ajax request method to the child component InputForm. The child component would then return the results, which will be stored in the state ...

Problem encountered while attempting to insert chunk data into an array correctly with Node.js

I am currently working on a project where I need to read a text file and store each word in an array using Node.js. However, I am facing difficulties as my current code is not producing the desired result. Below is an overview of my txt file. CHANGES: C ...

How can I display a Bootstrap modal in Ember.js after rendering it in an outlet?

I'm facing a challenge in my Ember.js application where I need to trigger the opening of a Bootstrap modal from my ApplicationRoute upon calling the openModal action. This is what my ApplicationRoute looks like: module.exports = Em.Route.extend({ ...

The Heroku app is having trouble communicating with the Mongo Atlas database, leading to frequent crashes

My deployed application on Heroku was working fine until I encountered an issue with the connection to my MongoDB Atlas database. Despite my attempts to resolve it, the application keeps crashing. The Heroku logs show the following: "2022-12-05T10:19: ...

How can I use jquery's data-dismiss to clear a selected file in Bootstrap?

My image uploader div includes a sub-div to display the chosen file and an <a> element that I aim to delete using jQuery. Has anyone successfully achieved this? I tried binding a click event to the <a>, but it didn't work, possibly due to ...

Displaying error messages for bad requests in a Web API using JSON and AJAX

I need assistance in displaying error messages from my child class on the front end of my website when using a REST client to return an 'ok' response. I have provided my create methods and controller below, any guidance would be greatly appreciat ...

Implementing a PHP/HTML caching system is a crucial step in optimizing website

Having delved into various guides about implementing a PHP cache system for my custom-coded, query-heavy, and expanding site, one resource I found useful is this one: While I grasp the concept, there are specific parts of my page that cannot be cached. Wh ...

Perform a function within another function in Vue

I am dealing with two nested functions in Vue. The parent function needs to retrieve the value of an attribute, while the child function is responsible for using this attribute value to make an API call. How can I ensure that both parts are executed simult ...

What is the best way to rearrange columns in bootstrapping version 3

In the process of developing a responsive website with the use of bootstrap 3, I am faced with the task of rearranging columns for the mobile version. The layout of the home page has been attached for reference, along with the desired layout for the mobi ...

What are the steps to implement server side routing using react-router 2 and expressjs?

If you're looking to delve into server side rendering with react-router, the documentation linked here might fall short in providing a comprehensive understanding. In particular, it may not offer enough insights on how to leverage react-router 2 for r ...

Tips for resizing the width automatically within a container?

I am facing an issue with a container that contains 3 DIVS: left sidebar, main content, and a right sidebar. I have made the main content responsive by setting the width to width:calc(100% - 400px); (where 400px is the combined width of the sidebars). Howe ...

Obtaining IDs of Divs that have been Dragged into a Drop Zone upon Clicking a Button using JavaScript

I'm currently working on a solution to extract the ids of dragged divs once they have been placed in the drop zone. Each drag component is assigned an id ranging from drag1 through drag8, while the drop zone is labeled as div drop zone. Since there a ...

The position of the camera remains static even after it is attached to a mesh

Looking to implement a TPS (third-person shooter) camera that follows the player? I have added the camera to a cube which represents the player's movement. You can view the coordinates of both the camera and cube in this example. While the camera is f ...

How can I retrieve a password entered in a Material UI Textfield?

My code is functioning properly, but I would like to enhance it by adding an option for users to view the password they are typing. Is there a way to implement this feature? const [email, setEmail] = useState(''); const [password, setPassword] = ...