简体   繁体   中英

jquery, if specific elements contains specific text, change the icon class before this

i have this html code:

<div class="online-users">
online users
    <span class="user-online">
         <i class="fa fa-circle"></i>
         <span class="user-name">Ster</span>
    </span>
    <span class="user-online">
         <i class="fa fa-circle"></i>
         <span class="user-name">dimitris</span>
    </span>
    <!-- ... more users... -->
</div>

this code showing which users are online. I need a code to change the icons "i" before users names. Every user have specific icon.

i have try with this jquery code:

<script>
$(document).ready(function(){
    $('.online-users span.user-name').each(function(){
        $(this:contains('Ster')).siblings('i').toggleClass('fa-solid fa-user-gear');
        $(this:contains('dimitris')).siblings('i').toggleClass('fa-solid fa-user-pen');
    });
});
</script>

But nothing change. Any idea what is wrong with my code?

The main error in your code was using a selector like this:

$(this:contains('Ster'))

You need to pass a string to the jQuery $ function if you want to return the result of a css selector.

I refactored better your code to achieve the desired result, so that there's a dedicated function to change the style, toggling those fontawesome css classes, of the <i> element next to an element containing the username you wish to style.

The selector I used to fetch the .user-name element containing a given string was:

$(`.online-users >.user-online >.user-name:contains('${username}')`)

That's a template string (and it's wrapped by upticks instead of single quotes or double quotes). It looks for the element .user-name in that defined hierarchy and containing the value hold in the username variable.

When document is ready, each username found gets styled like that.

...But the painful truth:

Your approach doesn't play well with every scenario. Consider if you have a list of users where there's one containing the other like: user and user2 . In that case the :contains selector will hit twice on the same element and will screw the style because you were toggling the class and not just adding it.

 $(document).ready(function(){ toggleUsersIconStyle(); }); function styleUser(username){ $(`.online-users >.user-online >.user-name:contains('${username}')`).siblings('i').toggleClass('fa-solid fa-user-gear'); } function toggleUsersIconStyle(){ $('.online-users span.user-name').each((i, username)=>{ styleUser( $(username).text() ); }); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" /> <div class="online-users"> online users: <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">Ster</span> </span> <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">dimitris</span> </span> <.--... more users... --> </div>

The problem with your own code is:

$(this:contains('Ster'))

The :contains() jQuery selector works – as does a CSS Selector – as part of a string, whereas you've attempted to couple it to a DOM node ( this ), which results in a syntax error:

Uncaught SyntaxError: missing ) after argument list

You could work your way around this – if you wanted to – by using:

// we need to wrap this in a jQuery Object to enable
// the use of jQuery methods:
$(this)
  // and then we can use filter() to check if the
  // <string> is present within the element and
  // if so we can then chain further methods:
  .filter(':contains("<string>")')

Which gives the following approach:

 $(document).ready(function() { $('.online-users span.user-name').each(function() { let userName = $(this).text(); $(this).filter(':contains("dimitris")').prev('i').toggleClass('fa-solid fa-user-pen'); $(this).filter(':contains("Ster")').prev('i').toggleClass('fa-solid fa-user-gear'); }); });
 *, ::before, ::after { box-sizing: border-box; font-size: 16px; margin: 0; padding: 0; }.online-users, .user-online { border: 1px solid currentColor; display: grid; gap: 0.25em; padding-block: 0.25em; padding-inline: 0.5em; }.online-users { inline-size: fit-content; margin-block: 1em; margin-inline-start: 1em; }.user-online { grid-template-columns: repeat(2, max-content); }
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="online-users"> online users <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">Ster</span> </span> <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">dimitris</span> </span> <.--... more users... --> </div>

JS Fiddle demo .

Or, instead of filter() , we could instead use the .is() method, which returns a Boolean ( true / false ) if the element matches, or does not match, the supplied CSS selector:

 $(document).ready(function() { $('.online-users span.user-name').each(function() { let userName = $(this).text(); if ($(this).is(':contains("dimitris")')) { $(this).prev('i').toggleClass('fa-solid fa-user-pen'); } else if ($(this).is(':contains("Ster")')) { $(this).prev('i').toggleClass('fa-solid fa-user-gear'); } }); });
 *, ::before, ::after { box-sizing: border-box; font-size: 16px; margin: 0; padding: 0; }.online-users, .user-online { border: 1px solid currentColor; display: grid; gap: 0.25em; padding-block: 0.25em; padding-inline: 0.5em; }.online-users { inline-size: fit-content; margin-block: 1em; margin-inline-start: 1em; }.user-online { grid-template-columns: repeat(2, max-content); }
 <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <div class="online-users"> online users <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">Ster</span> </span> <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">dimitris</span> </span> <.--... more users... --> </div>

JS Fiddle demo .

However it's worth noting that both of the previous approaches require you to code to explicitly check for each user-name, and hard-code those user-names and matching icons into the code. Not only does this require an update to the code each time a user is added, or removed and sets or changes their chosen icon the code itself has to check for every user; this leads to bloated code that's difficult to maintain.

Instead, I'd suggest the following approach, which requires one Object to be updated (which can be automatically generated on the back-end), and otherwise lets the code handle the checks automatically, this code has explanatory comments in the code:

 // An object mapping user-names to the icons to show for // the given user: const userIcons = { Ster: 'fa-user-gear', dimitris: 'fa-user-pen' } $(document).ready(function() { // selecting the <span class="user-name"> elements within the // '.online-users' element, and iterating over that collection // with the each() method: $('.online-users span.user-name').each(function() { // caching the current user-name, from the text of the current // element and removing leading/trailing white-space with // String.prototype.trim(): let userName = $(this).text().trim(); // accessing the previous <i> element sibling: $(this).prev('i') // and toggling the listed class-names, which uses a // a template literal to always toggle the 'fa-solid' // class-name, and interpolating the result of the // userIcons[userName] expression: .toggleClass(`fa-solid ${userIcons[userName]}`); }); });
 *, ::before, ::after { box-sizing: border-box; font-size: 16px; margin: 0; padding: 0; }.online-users, .user-online { border: 1px solid currentColor; display: grid; gap: 0.25em; padding-block: 0.25em; padding-inline: 0.5em; }.online-users { inline-size: fit-content; margin-block: 1em; margin-inline-start: 1em; }.user-online { grid-template-columns: repeat(2, max-content); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <div class="online-users"> online users <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">Ster</span> </span> <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">dimitris</span> </span> <.--... more users... --> </div>

JS Fiddle demo .

Or, in plain JavaScript:

 // As before, an Object used to map the user-specific icon(s) // to a named user: const userIcons = { Ster: 'fa-user-gear', dimitris: 'fa-user-pen' } // an alternative to jQuery's $(document).ready(): window.addEventListener('DOMContentLoaded', ()=>{ // retrieving a NodeList of all <span class="user-name"> elements // within the '.online-users' element, and iterating over that // NodeList with NodeList.prototype.forEach() and its anonymous // (Arrow) function: document.querySelectorAll('.online-users span.user-name').forEach( // passing in a reference to the current Node of the NodeList // over which we're iterating: (el)=>{ // caching the user-name from the current node, and // removing leading/trailing white-space with String.prototype.trim(): let userName = el.textContent.trim(), // caching a reference to the previousElementSibling node: previousSibling = el.previousElementSibling; // if the previous element matches the selector (here: 'i') supplied: if (previousSibling.matches('i')) { // we then use the Element.classList API to add the listed // comma-separated, class-names: previousSibling.classList.add('fa-solid', userIcons[userName]); } }); });
 *, ::before, ::after { box-sizing: border-box; font-size: 16px; margin: 0; padding: 0; }.online-users, .user-online { border: 1px solid currentColor; display: grid; gap: 0.25em; padding-block: 0.25em; padding-inline: 0.5em; }.online-users { inline-size: fit-content; margin-block: 1em; margin-inline-start: 1em; }.user-online { grid-template-columns: repeat(2, max-content); }
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha512-MV7K8+y+gLIBoVD59lQIYicR65iaqukzvf/nwasF0nqhPay5w/9lJmVM2hMDcnK1OnMGCdVK+iQrJ7lzPJQd1w==" crossorigin="anonymous" referrerpolicy="no-referrer" /> <div class="online-users"> online users <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">Ster</span> </span> <span class="user-online"> <i class="fa fa-circle"></i> <span class="user-name">dimitris</span> </span> <.--... more users... --> </div>

JS Fiddle demo .

References:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM