Step-by-step tutorial on designing an input slider to dynamically adjust the CSS :before linear-gradient values

Is there a way to achieve a gradient effect using pseudo-element :before on an image that can be customized by adjusting a slider input? I've been trying to implement this feature with the following code, but have not been successful so far.

var sliderValue = $('#range');

sliderValue.oninput = function(){
    var val1 = this.value;
    var val2 = 100-val1;     
    $('<style> #filter:before{linear-gradient(to top, rgba(255,255,255,1) '+val1+'%,rgba(0,0,0,0) '+val2+'%);}</style>').appendTo("#filter");
};
.slider
{
    -webkit-appearance:none;
    appearance:none;
    width: 100%;
    height: 10px;
    max-width: 400px;
    background-color: #ccc;
    margin: 0;
    padding: 0;
    outline: none;
    border-radius: 10px;
    cursor: pointer;
}
#filter {
    position:relative;
    float: left;
    max-height: 480px; 
    
}

#filter:before {
    content: "";
    position:absolute;
    top:0;
    left: 0;
    right: 0;
    bottom: 0;
    width:320px;
    height:100%;
    background: linear-gradient(to top, rgba(255,255,255,1) 15%,rgba(0,0,0,0) 22%);
    z-index: 1;
}
<div class="container">
   <input type="range" min="0" max="100" value="0" class="slider" id="range">    
   <div id="filter">  
    <img id="previewImg" src="img/dummy_320x480_ffffff_d002e2.png" alt="Placeholder" style="height: 100%; width:320px;">
   </div>
</div>  
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>

Answer №1

If you want the gradient to work properly, there are some adjustments that need to be made in the code:

  1. Ensure you are selecting the input element using jQuery's $(...) function instead of document.getElementById(...).
  2. Link the input element's oninput function to jQuery's input event.
  3. Normalize the slider value from 0 to 1 rather than 0 to 100.
  4. Make sure the gradient CSS declaration uses the correct notation for the color values (e.g., rgba(255,255,255,1) should be rgba(255,255,255,1.0)).

After making these changes, your code should look something like this:

HTML:

<div class="container">
  <input type="range" min="0" max="100" value="0" class="slider" id="range">    
  <div id="filter">
    <img id="previewImg" src="img/dummy_320x480_ffffff_d002e2.png" alt="Placeholder" style="height: 100%; width:320px;">
  </div>
</div>

jQuery:

var sliderValue = $('#range');
sliderValue.on('input', function() {
  var val1 = this.value / 100;
  var val2 = 1 - val1;
  $('<style> #filter:before {background: linear-gradient(to top, rgba(255,255,255,1.0) ' + val1*100 + '%, rgba(0,0,0,0) ' + val2*100 + '%);}</style>').appendTo("#filter");
});

CSS:

.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 10px;
  max-width: 400px;
  background-color: #ccc;
  margin: 0;
  padding: 0;
  outline: none;
  border-radius: 10px;
  cursor: pointer;
}

#filter {
  position: relative;
  float: left;
  max-height: 480px; 
}

#filter:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 320px;
  height: 100%;
  background: linear-gradient(to top, rgba(255,255,255,1.0) 15%, rgba(0,0,0,0) 22%);
  z-index: 1;
}

Remember that the tag is meant for declaring CSS styles in the HTML page header and not for dynamically creating styles in jQuery. In the provided code snippet, we used the appendTo method to insert the dynamically generated CSS style into the #filter element.

Answer №2

There are a few issues with the code you provided:

  • Instead of appending the new style to every input, I have created an HTML element for the style that gets updated with the correct content on every input event;
  • The color was not displaying properly, so I changed rgba(0,0,0,0) to rgba(0,0,0,.1) to ensure it is visible;
  • The #filter div was overlapping the slider, preventing activation. By removing the float left property, this issue has been resolved.

var sliderValue = $('#range'),
    filterStyle = $('#filter-before');
    
sliderValue.on('input', function() {
  var val1 = this.value;
  var val2 = 100 - val1;
  filterStyle.html('#filter:before{background: linear-gradient(to top, rgba(255,255,255,1) ' + val1 + '%,rgba(0,0,0,.1) ' + val2 + '%);}');
});
.slider {
  -webkit-appearance: none;
  appearance: none;
  width: 100%;
  height: 10px;
  max-width: 400px;
  background-color: #ccc;
  margin: 0;
  padding: 0;
  outline: none;
  border-radius: 10px;
  cursor: pointer;
}

#filter {
  position: relative;
  max-height: 480px;
  width: 200px;
  height: 200px;
}

#filter:before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  width: 320px;
  height: 100%;
  background: linear-gradient(to top, rgba(255, 255, 255, 1) 15%, rgba(0, 0, 0, 0) 22%);
  z-index: 1;
}
<style id="filter-before"></style>
<div class="container">
  <input type="range" min="0" max="100" class="slider" id="range">
  <div id="filter">
    <img id="previewImg" src="img/dummy_320x480_ffffff_d002e2.png" alt="Placeholder" style="height: 100%; width:320px;">
  </div>
</div>
<script src="https://code.jquery.com/jquery-3.6.3.min.js" integrity="sha256-pvPw+upLPUjgMXY0G+8O0xUf+/Im1MZjXxxgOcBQBXU=" crossorigin="anonymous"></script>

Answer №3

It has been pointed out in previous responses that there were several issues with your original code, mainly revolving around the attempts to append a <style> element to the document upon each input event triggered on the sliderValue element. Unfortunately, the sliderValue Object was a jQuery Object devoid of an oninput method.

To associate an event-handler with a jQuery Object, you should utilize the on() method:

sliderValue.on('<eventName>', function(){
  // functionality
});

Instead of delving into improved jQuery practices covered by others, I wanted to demonstrate how this could be accomplished using vanilla JavaScript and CSS custom-properties (with explanatory notes included in the code):

// utility functions for brevity:
const D = document,
  get = (selector, context = D) => context.querySelector(selector),
  getAll = (selector, context = D) => [...context.querySelectorAll(selector)];
  
// triggering an input-event once an event-handler is bound:
let inputEvent = new Event('input'),
    updateGradient = (evt) => {
        let updated = evt.currentTarget,
            target = get(updated.dataset.updates);
        
        target.style.setProperty(`--${updated.name}`, updated.value)
},
updaters = getAll('input[data-updates]');

updaters.forEach(
  (el) => {
    el.addEventListener('input', updateGradient);
    el.dispatchEvent(inputEvent);
  });
:root {
  --spacing: 1rem;
}

*,
::before,
::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html,
body {
  font-family: system-ui;
  font-size: 16px;
  font-weight: 400;
  min-block-size: 100%;
  padding-block: var(--spacing);
}

main {
  block-size: 100%;
  border: 1px solid #000;
  display: grid;
  gap: var(--spacing);
  inline-size: clamp(30rem, 80%, 1200px);
  margin-inline: auto;
}

form {
  display: grid;
  gap: var(--spacing);
  padding: var(--spacing);
}

label {
  display: flex;
  gap: var(--spacing);
}

.labelText {
  flex-grow: 1;
  text-align: end;
}

.labelText::after {
  content: ':';
}

label > input {
  flex-basis: 70%;
}

.preview {
  --color1: rgba(255 255 255 / 1);
  --color2: rgba(0 0 0 / 1);
  --stop: 50;
  aspect-ratio: 2;
  background-image:
    linear-gradient(
      180deg,
      var(--color1, lightskyblue)
      calc(var(--stop) * 1%),
      var(--color2, lime)
      calc(var(--stop) * 1%)
    );
}
<main>
  <form action="#" method="post">
    <label>
      <span class="labelText">Color 1</span>
      <input type="color" name="color1" value="#000000" data-updates=".preview">
    </label>
    <label>
      <span class="labelText">Color 2</span>
      <input type="color" name="color2" value="#ff9900" data-updates=".preview">
    </label>
    <label>
      <span class="labelText">Gradient stop (%)</span>
      <input type="range" name="stop" min="0" max="100" value="25" data-updates=".preview">
    </label>
    <button type="submit">Submit</button>
  </form>
  <div class="preview"></div>
</main>

View JS Fiddle demo.

References:

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

Uncovering the xpath of an element within an iframe using QTP

When attempting to set a value in the <input type="file" name="file007"> element using QTP, I encountered an issue. The element is located within an iframe, making it inaccessible through xpath on the page. <iframe id="file_007" src="javascript:& ...

Issue with reverse document referencing in Mongoose

I've been searching extensively for a way to reference documents bidirectionally in Mongoose. Despite all the documentation and examples provided in Populate, they only show how to save the objectID of one document in another. Let's say I have a ...

Tips for postponing the removal of a class in a Bootstrap 4 dropdown mega menu?

Currently, I am facing a challenge in toggling a class on a navbar toggle button. The issue arises when the cursor hovers over the dropdown menu, causing the class to be quickly removed and the menu to disappear. It seems like the solution lies in implemen ...

The Datalist feature in HTML5 offers an auto-suggest functionality that displays a list of options beginning with the

In my project, I am utilizing HTML5 Datalist for autosuggestion. By default, HTML5 follows the keyword contains approach rather than the starts with approach. For example, if my datalist includes one, two, three and I type "o" in the search box, it displ ...

The JSON and AJAX are functioning properly on Firefox, however, they are not working on Chrome

I have created a shopping cart using jquery, ajax, and json which works perfectly fine on my local server in Chrome. However, when I try to run it on the server using Chrome, it does not seem to be functioning properly; although it runs smoothly on Firefox ...

Refrain JavaScript - sift through an array of objects based on the values of a primitive array in linear time

I've got an array of objects that looks like this: [{ itemType: 'bottle', itemId: '111' }, { itemType: 'bottle', itemId: '222' }, { itemType: 'bottle', ...

Vue.js is throwing an error because it cannot find the property or method "blah" when referencing it during rendering

Can you explain why Vue 2 throws an error indicating that a prop is not defined, even though it is statically defined in the parent template? Note: This error does not occur when I include the JavaScript code within the .vue file's script tag instead ...

Universal message/status/error that can be utilized in both jquery and C# programming

There are instances where we utilize the same error or success messages, as well as checking for certain statuses, in both jQuery and C#. To maintain consistency, it is ideal to define all message/status flags within a static class and access them whereve ...

Developing a matrix arithmetic parser using JavaScript

Currently, I am in the process of developing a program that can solve matrix equations. My main focus right now is on making sure the parser functions correctly. However, I am feeling lost and unsure of where to begin. In my program, I utilize an array of ...

Only send file input to the server when it is changed, and disregard any other inputs until the form is submitted

I am working with a form structured like this: <form method="post" action=""> <input type="text" onchange="uploadFile()" name="username"> <textarea name="description">< ...

Master the art of sending multiple asynchronous requests simultaneously with suspense in Vue 3

Utilizing <Suspense>, I am handling multiple requests in my child component using the await keyword: await store.dispatch("product/getProduct", route.params.id).then(res => productData.value = res); await store.dispatch("product/get ...

Unusual Display of Mantine Radio Button

I am currently using Mantine v7.0.0 in combination with Next.js v13.5.2 and Tailwind v3.3.3. In my setup, when I create <Radio /> elements, the svg of the element is appearing separately from the radio button instead of replacing it. This issue can b ...

SWF file redirection with a flash blast

How can I implement a click event and redirect on an embedded SWF object? I currently have the following code, but it doesn't appear to be working: <object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" codebase="http://download.macromedia. ...

Steps for formatting and retrieving the <style jsx> string from a function within a React or Nextjs application

In my quest to discover a method of "organizing" react components similar to Vue, where they are packaged together with separate sections for HTML, JS, and CSS. For instance export default function HeaderExample() { return ( <> ...

Guide to organizing an express.js project structure:

When working with an Express.js application, what are the typical strategies for breaking up and modularizing the app.js file? Do developers usually keep everything in a single file, or is there a common convention for splitting it into smaller modules? ...

Encountering a React.js issue when attempting to update data

When attempting to update fields with a given drugid, an error occurs. For example, selecting drugid as 1 and clicking the update button results in an error message stating 'localhost:8081/drug/1' not found. In the MongoDB database, there is also ...

Eliminate the bottom border from the MUI Input component

Is there a way to get rid of the bottom-line borders on these input fields? I attempted using CSS but couldn't find a solution for this component -> <Input type={"text} /> ? ...

Build desktop-style applications using a unique user interface library and integrated development environment designed specifically for HTML, JavaScript

I am looking to create a desktop application using HTML, JavaScript, and AJAX for the purpose of making it available on multiple platforms. I was wondering if there is a UI library and web IDE similar to WebOS Enyo that I can use to build my HTML/AJAX app? ...

Organizing AngularJS ng-repeat into sets of n items

I am facing a challenge with a data set structured like this: $scope.items = [ { model:"A", price: 100, quantity: 30}, { model:"B", price: 90, quantity: 20 }, { model:"C", price: 80, quantity: 200 }, { model:"D", price: 70, quantity: 20 ...

The Vue Swiper does not support inline containers in my code

I am facing an issue with the layout of my simple swiper in my vue app. The containers are not inline as expected, causing the second one to appear under the first. Additionally, I am struggling with making the slider element visible only within the viewpo ...