I have this great reference here. I'm trying to toggle a class and will affect 1 at a time. But it seems to be not working without using a parent element to click just like in the reference. I want to use only 1 class with a click function and will toggle effect one at a time. Would be this possible?
$(document).ready(function() {
getQuoteButtton();
});
function getQuoteButtton() {
// button quote
$("body").on("click", ".btn-quote", function(e) {
var $this = $(this);
$this.toggleClass('quote-selected');
if ($this.hasClass('quote-selected')) {
$this.text('Selected');
} else {
$this.text('Select This Quote');
}
});
}
.section-block-table {
width: 100%;
margin: 0 auto;
position: relative;
border-collapse: separate;
color: #2E384D;
border-spacing: 0;
}
.btn-quote {
background: #fff;
position: relative;
text-align: center;
color: #49CD96;
font-size: 14px;
font-weight: 600;
line-height: 20px;
padding: 8px 9px;
border-radius: 6px;
border: 1px solid #49CD96;
min-width: 166px;
}
.btn-quote:hover {
background: #49CD96;
color: #fff;
}
.btn-quote.quote-selected {
background: #49CD96;
color: #fff;
}
.btn-quote.quote-selected:before {
content: "\f00c";
font-family: 'Font Awesome 5 Free';
color: #fff;
margin-right: 7px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<table class="section-block-table">
<tr>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
</tr>
</table>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
It is recommended to delegate. Why do you not want to use the parent element?
Note I added a recommended tbody and gave it an ID
$(function() {
getQuoteButtton()
})
function getQuoteButtton() {
const $tb = $('#tb');
$tb.on('click', '.btn-quote', function(e) {
$this = $(this);
$this
.toggleClass('quote-selected')
.text($this.hasClass('quote-selected') ? 'Selected' : 'Select This Quote');
$('.btn-quote', $tb).not(this)
.removeClass('quote-selected')
.text('Select This Quote');
});
}
.section-block-table {
width: 100%;
margin: 0 auto;
position: relative;
border-collapse: separate;
color: #2E384D;
border-spacing: 0;
}
.btn-quote {
background: #fff;
position: relative;
text-align: center;
color: #49CD96;
font-size: 14px;
font-weight: 600;
line-height: 20px;
padding: 8px 9px;
border-radius: 6px;
border: 1px solid #49CD96;
min-width: 166px;
}
.btn-quote:hover {
background: #49CD96;
color: #fff;
}
.btn-quote.quote-selected {
background: #49CD96;
color: #fff;
}
.btn-quote.quote-selected:before {
content: "\f00c";
font-family: 'Font Awesome 5 Free';
color: #fff;
margin-right: 7px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<table class="section-block-table">
<tbody id="tb">
<tr>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
<td><button class="btn-quote m-auto">Select This Quote</button></td>
</tr>
</tbody>
</table>
<iframe name="sif2" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
CodePudding user response:
To answer to the OP's question, "it seems to be not working without using a parent element to click just like in the reference", it can be done, like below. What is needed is some logic to remove the selected status from the previously selected button.
The strength of event delegation comes from the fact that it allows to listen to an event on elements created at runtime, as the event will bubble up to the element the listener is attached to. In other words, attaching the listeners directly to the button will work if and only if all the buttons needed during the app lifetime are already present in the dom when the listener is declared.
If that is not the case, as would happen for instance when a button is added by javascript after dom content is loaded, delegating to an ancestor element is the solution.
mplungjan's answer is more robust in general, there can indeed be cases where attaching the listener directly to the element is the way to go.
$(document).ready(function() {
$('button[role=option]').click(toggleSelected)
})
function toggleSelected(event) {
// as we are listening to an event
// event.target is the element that
// was clicked by the user
const $clickedButton = $(event.target)
const $otherButtons = $('button[role=option]').not(event.target)
// we can use 'aria-pressed' accessibility attribute
// to represent the state of the button
const newSelectedState = ! ($clickedButton.attr('aria-pressed') === 'true')
$clickedButton
.attr('aria-pressed', newSelectedState)
.text( newSelectedState
? 'Selected'
: 'Select This Quote'
)
$otherButtons
.attr('aria-pressed', false)
.text('Select This Quote')
}
button{
color: dimgray;
}
button[aria-pressed=true]{
color: black;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<table>
<tbody>
<tr role="listbox">
<td><button role="option">Select This Quote</button></td>
<td><button role="option">Select This Quote</button></td>
<td><button role="option">Select This Quote</button></td>
</tr>
</tbody>
</table>
<iframe name="sif3" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>
A couple of notes:
Be aware of the peculiarity of the keyword this
inside javascript functions, as its behavior is dictated by the functional paradigm underlying the language and it can be confusing if compared to how it behaves in other OOP languages.
There are other tags that can represent a list of options directly, namely <select>
with a list of nested <option>
or <input type=radio>
.
In this implementation, adding role
tags to the options and to the containing element helps to define the semantic value of the html markup.
Accessible Rich Internet Applications
To represent the state of the button the attribute aria-pressed
can be used. Again, when possible, it is advisable to use semantically significant tags.
In this example no css class is needed.