How can the "Search within page" feature locate words that are divided between different elements?

I need to present text in groups of a specific length while still being able to search for text that spans multiple groups on the page. Additionally, I want to include a character above each group.

My current approach is as follows:

  body {
    margin-top: 1rem;
    font-family: mono;
  }

  br {
    margin-bottom: 2rem;
  }

  span {
    margin-right: 1rem;
    position: relative;
  }

  .absolute-before::before {
    content: "⇣";
    position: absolute;
    top: -1.2rem;
    left: 0;
    color: red;
  }

  .static-before::before {
    content: "⇣";
    color: green;
  }
   <span class="absolute-before">This_is_a_sp</span><span class="absolute-before">lit_string</span>

   <br />

   <span class="static-before">This_is_a_sp</span><span class="static-before">lit_string</span>

Note: the string and split position are hard-coded here, but are dynamically calculated in the real case.

The first method (using red arrows) visually works fine, but there is an issue with searching for text that spans across groups, particularly in Firefox where the search does not yield results but works in Chromium.
On the other hand, the second method (with green arrows) does not provide a strong visual effect but the search functionality works well.
The problem seems to be related to using position: absolute in the first method's ::before pseudo-element, causing the search feature to break.

Do you have any suggestions on how to achieve this? I am open to different approaches (especially excluding the ::before method), so any ideas are appreciated! It is essential to maintain compatibility with the browser's native Search in page... tool.

Edit:

I came across a Bugzilla bug that also addresses this issue, suggesting that resolving it through the ::before method may be unlikely. Any alternative solutions are still welcomed!

Answer №1

In order to ensure the browser's native search feature works properly, it may be necessary to adjust how content is displayed in different browsers. While Firefox and Chromium handle searching content spanning among adjacent elements differently, one potential solution is to concatenate the entire content upfront and use JavaScript to add arrow indicators at specified letter positions for accurate searching.

It should be noted that the specific string and split position are hardcoded here for demonstration purposes, but would typically be dynamically calculated based on real data.

Using letter positions for arrow indicators may not always be reliable, but currently, this method seems to offer the most effective approach.

The key was identifying the coordinates of a given letter position and using that information to position a newly created arrow element over the content div.

A monospaced font was chosen as it aligns well with this strategy.

This demo also accounts for scenarios where the content overflows its container, triggering word wrap. Understanding that the content consists of a continuous stream of letters without spaces, I implemented word-wrap: break-word; to ensure words are broken if no spaces are detected.

As the arrows' positions are dependent on the text rendering during their creation, an observer is utilized to redraw the arrows whenever the container size changes (e.g., window resizing). This ensures the arrows remain fixed to their respective letters despite any resizing actions.

An unintended consequence of this method is the lack of spacing between fragments as shown in the example provided. If maintaining space between fragments is crucial, this solution may not be viable.

//initializing page content
document.addEventListener('DOMContentLoaded', ()=>{  
  //selecting the content container
  const contentDiv = document.querySelector('.content');
  //drawing arrows within the container
  redrawArrows(contentDiv);
  
  //setting up an observer to redraw arrows upon container resize
  const resizeObserver = new ResizeObserver(()=>{redrawArrows(contentDiv);});
  resizeObserver.observe(contentDiv);
})

//adding an arrow above a specified letter position within an element
function addArrowAbovePosition(element, position) {
    //determining the position of the specified letter within the element
    const range = document.createRange();
    range.setStart(element.firstChild, position);
    range.setEnd(element.firstChild, position + 1);
    const rect = range.getBoundingClientRect();

    //creating the arrow element
    const arrow = document.createElement('div');
    arrow.classList.add('arrow');    
    arrow.innerHTML = '⇣';    
    arrow.style.left = rect.left + 'px';
    arrow.style.top = (rect.top - rect.height) + 'px';    
    
    //appending the arrow to the element
    element.appendChild(arrow);
}

//redrawing arrows within an element
function redrawArrows(element){
  //removing any existing arrows
  element.querySelectorAll('.arrow')
    .forEach(arrow => arrow.remove());

  //adding arrows at custom positions (2, 10, 87)
  addArrowAbovePosition(element, 2); 
  addArrowAbovePosition(element, 10); 
  addArrowAbovePosition(element, 87);
}
body {
  margin-top: 1rem;
  /*monospaced font will work better in this scenario*/
  font-family: monospace;
  font-size: 20px;
}

.arrow{
  position: absolute;
  color: red;    
}

.content{
  /*word wraps also content not having spaces*/
  word-wrap: break-word;
  /*allows arrows to have their own space among text lines*/
  line-height: 2em;
}
<div class="content">
This_is_a_very_long_text_to_highlight_a_scenario_where_the_content_will_word_wrap_no_matter_what_even_if_theres_no_space_in_between_words
</div>

Answer №2

Based on the findings, it appears that the Firefox issue stems from the pseudo element positioning itself in between the two words. A potential workaround is to use :before for the first span and :after for the second span, which should resolve the search functionality problem in Firefox.

Here's an updated example:

body {
  margin-top: 1rem;
}

p {
  display: inline-block;
}

span {
  margin-right: 1rem;
  position: relative;
}

span.after::after,
span.before::before {
  content: "⇣";
  position: absolute;
  top: -1.2rem;
  left: 0;
  color: red;
}
<span class="before">This_is_a_sp</span><span class="after">lit_string</span>

However, one limitation of this solution is that you are restricted to only using 2 spans. For instance, trying something like:

<span class="before">This_is_a_sp</span><span class="after">lit_stri</span><span class="before">ng</span>

Will not work as intended, as the pseudo element will once again interfere with the positioning of the words. To illustrate this further:

https://i.sstatic.net/XFM0s.png

Answer №3

Initially, my approach involved grouping the text contents together to facilitate searching throughout.

However, a different user (@LS_) introduced a new perspective that inspired me. By still utilizing individual spans to group content and implementing a unique strategy with absolute positioned arrow divs, we can maintain search functionality for the combined text without disrupting the flow.

The key here is to create arrow divs positioned relative to the overall container, ensuring seamless search capability as if the text were contiguous.

Summary:

This code comprises an HTML structure with a

<div class="container">
encapsulating
<span class="fragment">
elements. Upon page load, the JavaScript dynamically generates arrows above each of these span.fragment elements, enabling effective search across the text as if they were part of a continuous string. Note!! Adjacent fragments will be regarded as a single word when on the same line without newlines!

//on page ready
document.addEventListener('DOMContentLoaded', ()=>{  
  //this is the container target
  //NOTE! I'm using querySelector on a classname.. it will return only the first element having that class
  const containerDiv = document.querySelector('.container');
  
  //it draws the arrows inside the target on top of each children span
  redrawArrows(containerDiv);
  
  //sets up an observer that will redraw the arrows when the target container is resized
  const resizeObserver = new ResizeObserver( ()=>{
   //using a delay of 50ms before redrawing (to be 100% sure it won't mess up with coords too early)
   setTimeout(() => {
        redrawArrows(containerDiv);
    }, 50);    
  } );
  resizeObserver.observe(containerDiv);
})

function redrawArrows(container){  

  //remove any existing arrows
  container.querySelectorAll('.arrow')
    .forEach(arrow => arrow.remove());

  //for each .fragment in container, adds an arrow in top
  container.querySelectorAll('.fragment')
    .forEach(fragment => addArrowOnTopOfSpan(container, fragment));
}

function addArrowOnTopOfSpan(container, span){  
  document.body.offsetHeight; // Force reflow
  
  //creates a new div to render the arrow
  const arrow = document.createElement('div');
  arrow.classList.add('arrow');    
  arrow.innerHTML = '⇣';    
  arrow.style.left = `${span.offsetLeft}px`;
  //the y coord is shifted 1em upward
  arrow.style.top = `calc( ${span.offsetTop}px - 1em )`;    
  
  container.appendChild(arrow);
}
body {
  margin-top: 1rem;
}

.container{
  position: relative;
}

.fragment{
  margin-right: 1em;
  margin-top: 1em;
  /*this is important for the offset coords to work as expected!*/
  display: inline-block;
}

.arrow{
  position: absolute;
  color: red;
}

.note{
  margin-top: 2em;
}
<div class="container">
  <span class="fragment">This_is_a_very_long_text_to</span><span class="fragment">highlight_a_scenario_where</span><span class="fragment">the_content_will_word_wrap</span><span class="fragment">no_matter_what_even_if_theres</span><span class="fragment">no_space_in_between_words</span>
</div>

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

Evaluating QUnit Test Cases

How do you write a test method in QUnit for validating functions developed for a form? For example, let's consider a form where the name field should not be left blank. If the validation function looks like this: function validNameCheck(form) { if ...

Uncovering a specific element within an array using knockout techniques

I am attempting to set a variable to an element within an array that holds a particular value for a property. For instance, I want to retrieve the item with an "Id" value of 15. However, my current method only retrieves the first item in the array, regar ...

Having issues with my toggler functionality. I attempted to place the JavaScript CDN both at the top and bottom of the code, but unfortunately, it is still not

I recently attempted to place the JavaScript CDN at the top of my code, but unfortunately, it did not have the desired effect. My intention was to make the navigation bar on my website responsive and I utilized a toggler in the process. While the navbar di ...

After running assets:install and assetic:dump, Font Awesome fonts are no longer functioning properly

Setting up a site on shared hosting has been going smoothly, except for the FontAwesome icons which Symfony is having trouble finding in their designated locations. The steps I followed to move the site to production on shared hosting are: To overcome th ...

Ensure that the TextBox field extends to the edges of the parent div, with full cross-browser compatibility

In the following code snippet, there is a challenge in ensuring that the TextBox field properly fits within the Width and Height of the div#chat-message. This seems to work well in Chrome but encounters issues in IE8 or Mozilla. Additionally, in Mozilla, t ...

Providing an HTML application even in the absence of an internet connection

I am currently developing an application that needs to function offline. I have experimented with the HTML5 manifest feature, which is now considered deprecated, and have also explored using 'Service Workers'. However, my challenge is that the ap ...

"Is there a way to verify the existence of checkbox array values within a database using PHP Laravel

In order to display checkboxes for each country that exists in the database, I need to verify if the countries in the database match with those in the list. Since the values are retrieved directly from the database and can't be set as input values due ...

Challenge with the contrast of text color on dark mode with a backdrop of light color texture (image)

Currently, I am using Bootstrap 4 and I have set a light coloured .png image as the background for the navbar and body. The footer area has a dark background with off-white text colour. By default, the text colour is black to complement the light-coloured ...

What is preventing me from concealing my input field when its type is set to "file"?

I've been attempting to conceal my input field of a file type, but even with the hidden attribute, it refuses to hide. https://i.sstatic.net/jyMrn.png https://i.sstatic.net/P59wV.png Interestingly, once I remove type="file", the code succe ...

Use jQuery to create a price filter that dynamically sets slide values based on the filter object

I'm currently utilizing the jQuery slide filter to sort products based on price. The filtering value is set within the script, but I am interested in dynamically setting it based on the highest/lowest number found on my page using a data attribute. D ...

Issues arise in mobile Safari when attempting to load JavaScript files, resulting in

Encountering an issue with my Angular app on mobile Safari during the initial load, where it hangs on a blank page for nearly 2 minutes. Interestingly, the app functions perfectly on other browsers, including Mac Safari and iPad Safari. Initially, I suspe ...

Hover your mouse over the menu to activate a timeout feature

Having an issue with my multi-level menu - when I hover over a main menu item, the sub-menu items are supposed to display. However, if I move the mouse too quickly from the main item to the sub-item and it goes off the path, the sub-menu disappears and I h ...

The dimension of HTML on an IOS web application

After successfully designing a web app that works well on desktop browsers and iPad Safari, I encountered an issue when trying to install it as a hybrid app using cordova 3.3.0. The problem arises with the height not displaying properly. Despite including ...

Detecting Collisions in a Canvas-Based Game

As part of my educational project, I am developing a basic shooter game using HTML5 canvas. The objective of the game is to move left and right while shooting with the spacebar key. When the bullets are fired, they travel upwards, and the enemy moves downw ...

Initializing an HTML5 video player directly from an Array

I am trying to populate a video player on my webpage with an array of videos. Here is the code I have so far: var current = 0; var videos = ["01", "02", "03", "04"]; function shuffle(array) { var currentIndex = array.length, temporaryValue, randomInde ...

Tips for preserving CSS styling following a hover event

While working on implementing a Menu control using jQuery and CSS, I encountered the following issue: Here is a live demo that demonstrates my current problem When I hover over 'Menu Item 1', this item successfully changes its style (background ...

Unable to create account using PHP

Every time I attempt to create an account, the data I receive is always problematic. The username must be between 3 and 15 characters I find it frustrating that the account creation never goes through successfully. What baffles me even more is that af ...

Achieve a vertical fill effect within a parent container using CSS to occupy the remaining space

I have set my sights on a specific goal. https://i.sstatic.net/BqqTX.jpg This represents my current progress in achieving that goal. https://i.sstatic.net/unQY9.jpg On the left side, I have an image with a fixed height. The aqua color fills the left s ...

Trouble with CSS animations in ThreeJS's CSS3DRenderer

Seeking a way to incorporate plain text (2D or 3D) into a threeJS scene while applying CSS effects, I stumbled upon this resource. It seemed to offer the solution I was looking for. However, when I tried to apply an animate.css class (a well-known CSS anim ...

Can you explain the HTML semantic layout for having multiple titles matched with their respective lists?

In my directional segment, I have included details such as the address, phone number, email, and service date/time. I envision it looking like the example below: https://i.sstatic.net/5h4SM.jpg Currently, my code appears as follows: <div class="co ...