I'm writing the code to edit a database table.
I have the following HTML:
<div id="1">
<div contenteditable>aaa</div>
<div contenteditable>bbb</div>
<div contenteditable>ccc</div>
<button onClick="a('save')">SAVE</button>
<button onClick="a('delete')">DELETE</button>
</div>
<div id="2">
<div contenteditable>ddd</div>
<div contenteditable>eee</div>
<div contenteditable>fff</div>
<button onClick="a('save')">SAVE</button>
<button onClick="a('delete')">DELETE</button>
</div>
<div id="3">
<div contenteditable>ggg</div>
<div contenteditable>hhh</div>
<div contenteditable>iii</div>
<button onClick="a('save')">SAVE</button>
<button onClick="a('delete')">DELETE</button>
</div>
And so on.
Using the following function, I can get the clicked button:
function a(value) {
console.log(value);
}
When a button (SAVE or DELETE) is clicked, I need to retrieve:
the id of the "parent" div;
the content of each of the three contenteditable divs inside the same "parent" div.
Is it possible using pure Javascript?
Any suggestion will be very appreciated.
Thanks in advance.
What I would do is implement click listeners in JS, that way I can query elements easily.
Here is the example:
// Query all div.div-editable elements
document.querySelectorAll('div.div-editable')
.forEach((div) => {
// The id of the parent
const divId = div.id;
// Each of content editable divs inside the parent div
const editables = div.querySelectorAll('div[contenteditable]');
// The buttons Save and Delete
const saveBtn = div.querySelector('button.button-save');
const deleteBtn = div.querySelector('button.button-delete');
// Add click listeners to buttons
saveBtn.addEventListener('click', function() {
console.log('Saved: ' + divId);
const contentOfEditableDivs = Array.from(editables).map((div) => div.innerText);
console.log('Values of divs:', contentOfEditableDivs);
});
deleteBtn.addEventListener('click', function() {
console.log('Deleted: ' + divId);
const contentOfEditableDivs = Array.from(editables).map((div) => div.innerText);
console.log('Values of divs:', contentOfEditableDivs);
});
});
<div id="1" class="div-editable">
<div contenteditable>aaa</div>
<div contenteditable>bbb</div>
<div contenteditable>ccc</div>
<button class="button-save">SAVE</button>
<button class="button-delete">DELETE</button>
</div>
<div id="2" class="div-editable">
<div contenteditable>ddd</div>
<div contenteditable>eee</div>
<div contenteditable>fff</div>
<button class="button-save">SAVE</button>
<button class="button-delete">DELETE</button>
</div>
<div id="3" class="div-editable">
<div contenteditable>ggg</div>
<div contenteditable>hhh</div>
<div contenteditable>iii</div>
<button class="button-save">SAVE</button>
<button class="button-delete">DELETE</button>
</div>
EDIT 1: Added code snippet
EDIT 2: Simplified explanation
You can send this keyword in the argument of click's event handler and then access the parent div's id.
So your HTML would look something like:
// rest of the code here
<button onClick="a(this, 'save')">SAVE</button>
<button onClick="a(this, 'delete')">DELETE</button>
// rest of the code here
And your JS code would change to:
function a(elem, value) {
console.log(elem.parentNode.id);
}
More details on the following link:
how i get parent id by onclick Child in js
I'm encountering a typical situation while accessing the innerHTML property using jQuery. I've fetched the target button using the jQuery attribute selector.
Below is the snippet of jQuery attribute selector.
jQuery('button[type="button"][class="btn btn-primary"]').each(function () {
var btn = jQuery(this);
console.log(btn);
if (btn[0].innerHTML === "OK") {
console.log("ok");
jQuery(this).click();
}
});
Following is the screenshot of the console log of the target button. It's innerHTML property is set to OK.
Following is the screenshot of the value of the innerHTML while debugging the target button object. In this case the value is "".
Ideally, the values of the innerHTML should be the same for both the cases.
EDIT
Why does this behavior differ that the ideal one? For both of the cases, the value of the innerHTML should be the same.
Also, there were multiple buttons. I have taken screenshots of different buttons. Thus their ID's are different. But still, the behavior is same.
Try something like this.
function SomeEvent($ele) {
alert($ele.html());
return false;
}
function invokeBtnEvents() {
$("[data-action=some-event]").off(); // clear old events
$("[data-action=some-event]").on("click", function() {
var $this = $(this);
return SomeEvent($this);
}) // define event(s)
return false;
}
$(document).ready(function() {
invokeBtnEvents();
});
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event" class="btn btn-primary">Button 1</button>
<button data-action="some-event" class="btn btn-primary">Button 2</button>
<button data-action="some-event" class="btn btn-primary">Button 3</button>
<button data-action="some-event" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>
Your issue
What you are doing that I think is your main issue is the $.each() method.
Store the buttons in a variable,
let $button = $("button"); // this returns an array of all the buttons on the DOM
You can then use the $.each() method to get the clicked element
$.each($button, function(index, btn){
const $this = $(btn);
$this.off();
$this.click(function(){someEvent($this)});
});
I would not invoke button clicks like this because every time you click a button, this each loop gets ran. It will then send all of the buttons to that action unless you parse by an ID or something (you are using the innerText).
If you use the code in my snippet, only the clicked button will be triggered.
An alternative approach to my first snippet is using something like a dispatcher.
function DoActionOneClick($ele){
alert("Action 1 " + $ele.html());
}
function DoDefaultClick($ele){
alert("Default Action " + $ele.html());
}
function DispatchEvent(action, $ele){
switch(action){
case "some-event-1":
DoActionOneClick($ele);
break;
default:
DoDefaultClick($ele);
break;
}
}
function invokeActions(){
$("[data-action]").off();
$("[data-action]").on("click", function(){
// get this
var $this = $(this);
// get action
var action = $this.data().action;
DispatchEvent(action, $this);
});
}
$(document).ready(function(){
invokeActions();
})
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="container">
<div class="row">
<div class="col-12">
<br />
<button data-action="some-event-1" class="btn btn-primary">Button 1</button>
<button data-action="some-event-2" class="btn btn-primary">Button 2</button>
<button data-action="some-event-2" class="btn btn-primary">Button 3</button>
<button data-action="some-event-2" class="btn btn-primary">Button 4</button>
</div>
</div>
</div>
Whenever I remove a dom element that precedes an element that has a ng-click attribute specified, it will no longer call the function that the ng-click references.
Here is an example of it not working. Note: if you change if(true) to if(false) and click save it will properly call the function.
function MainCtrl($scope) {
$scope.submit = function() {
alert('submitted');
}
function load() {
if(true){
$('#resetPassword').remove();
}
}
load();
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div ng-app>
<div ng-controller="MainCtrl">
<div class="btn-group m-b-20 pull-right" role="group">
<button type="button" id="resetPassword" class="btn btn-success">Reset Password</button>
<button type="button" class="btn btn-success" ng-click="submit();">Save</button>
</div>
</div>
</div>
I don't know why this is happening, but you should not use jQuery. Use ng-if to add or remove the button on condition. That's the way to go with Angular. jQuery's remove here looks dirty. ng-show just show or hides, but ng-if adds or removes the element from the DOM. It's what you want, and by far the simplest solution.
You should try to accomplish this with a custom directive. E.g:
JS
app.directive("removeClick", function() {
return {
link:function(scope,element,attrs)
{
element.bind("click",function() {
element.remove();
});
}
}
});
HTML
<div ng-app>
<div ng-controller="MainCtrl">
<div class="btn-group m-b-20 pull-right" role="group">
<button type="button" id="resetPassword"class="btn btn-success">Reset Password</button>
<button remove-click type="button" class="btn btn-success" ng-click="submit();">Save</button>
</div>
</div>
</div>
You are free to use getElementbyId at this point, although you should probably try to pass the correct element to your directive. This will probably give you a basic understanding of angular directives.
https://docs.angularjs.org/guide/directive
I am using Twitter Bootstrap to create collapsible sections of text. The sections are expanded when a + button is pressed. My html code as follows:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button type="button" class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
Is there a way to change the button to display - instead of + after the section is expanded (and change back to + when it is collapsed again)?
Additional information: I hoped there would be a simple twitter-bootstrap/css/html-based solution to my problem. All responses so far make use of JavaScript or PHP. Because of this I want to add some more information about my development environment: I want to use this solution inside a SilverStripe-based (version 3.0.5) website which has some implications for the use of both PHP as well as JavaScript.
try this. http://jsfiddle.net/fVpkm/
Html:-
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
JS:-
$('button').click(function(){ //you can give id or class name here for $('button')
$(this).text(function(i,old){
return old=='+' ? '-' : '+';
});
});
Update With pure Css, pseudo elements
http://jsfiddle.net/r4Bdz/
Supported Browsers
button.btn.collapsed:before
{
content:'+' ;
display:block;
width:15px;
}
button.btn:before
{
content:'-' ;
display:block;
width:15px;
}
Update 2 With pure Javascript
http://jsfiddle.net/WteTy/
function handleClick()
{
this.value = (this.value == '+' ? '-' : '+');
}
document.getElementById('collapsible').onclick=handleClick;
Here's another CSS only solution that works with any HTML layout.
It works with any element you need to switch. Whatever your toggle layout is you just put it inside a couple of elements with the if-collapsed and if-not-collapsed classes inside the toggle element.
The only catch is that you have to make sure you put the desired initial state of the toggle. If it's initially closed, then put a collapsed class on the toggle.
It also requires the :not selector, so it doesn't work on IE8.
HTML example:
<a class="btn btn-primary collapsed" data-toggle="collapse" href="#collapseExample">
<!--You can put any valid html inside these!-->
<span class="if-collapsed">Open</span>
<span class="if-not-collapsed">Close</span>
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
Less version:
[data-toggle="collapse"] {
&.collapsed .if-not-collapsed {
display: none;
}
&:not(.collapsed) .if-collapsed {
display: none;
}
}
CSS version:
[data-toggle="collapse"].collapsed .if-not-collapsed {
display: none;
}
[data-toggle="collapse"]:not(.collapsed) .if-collapsed {
display: none;
}
JS Fiddle
Add some jquery code, you need jquery to do this :
<script>
$(".btn[data-toggle='collapse']").click(function() {
if ($(this).text() == '+') {
$(this).text('-');
} else {
$(this).text('+');
}
});
</script>
All the other solutions posted here cause the toggle to get out of sync if it is double clicked. The following solution uses the events provided by the Bootstrap framework, and the toggle always matches the state of the collapsible element:
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button id="intro-switch" class="btn btn-success" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
JS:
$('#intro').on('show', function() {
$('#intro-switch').html('-')
})
$('#intro').on('hide', function() {
$('#intro-switch').html('+')
})
That should work for most cases.
However, I also ran into an additional problem when trying to nest one collapsible element and its toggle switch inside another collapsible element. With the above code, when I click the nested toggle to hide the nested collapsible element, the toggle for the parent element also changes. It may be a bug in Bootstrap. I found a solution that seems to work: I added a "collapsed" class to the toggle switches (Bootstrap adds this when the collapsible element is hidden but they don't start out with it), then added that to the jQuery selector for the hide function:
http://jsfiddle.net/fVpkm/87/
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button id="intro-switch" class="btn btn-success collapsed" data-toggle="collapse" data-target="#intro">+</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...<br>
<a id="details-switch" class="collapsed" data-toggle="collapse" href="#details">Show details</a>
<div id="details" class="collapse">
More details...
</div>
</div>
</div>
JS:
$('#intro').on('show', function() {
$('#intro-switch').html('-')
})
$('#intro').on('hide', function() {
$('#intro-switch.collapsed').html('+')
})
$('#details').on('show', function() {
$('#details-switch').html('Hide details')
})
$('#details').on('hide', function() {
$('#details-switch.collapsed').html('Show details')
})
I liked the CSS-only solution from PSL, but in my case I needed to include some HTML in the button, and the content CSS property is showing the raw HTML with tags in this case.
In case that could help someone else, I've forked his fiddle to cover my use case: http://jsfiddle.net/brunoalla/99j11h40/2/
HTML:
<div class="row-fluid summary">
<div class="span11">
<h2>MyHeading</h2>
</div>
<div class="span1">
<button class="btn btn-success collapsed" data-toggle="collapse" data-target="#intro">
<span class="show-ctrl">
<i class="fa fa-chevron-down"></i> Expand
</span>
<span class="hide-ctrl">
<i class="fa fa-chevron-up"></i> Collapse
</span>
</button>
</div>
</div>
<div class="row-fluid summary">
<div id="intro" class="collapse">
Here comes the text...
</div>
</div>
CSS:
button.btn .show-ctrl{
display: none;
}
button.btn .hide-ctrl{
display: block;
}
button.btn.collapsed .show-ctrl{
display: block;
}
button.btn.collapsed .hide-ctrl{
display: none;
}
My following JS solution is better than the other approaches here because it ensures that it will always say 'open' when the target is closed, and vice versa.
HTML:
<a href="#collapseExample" class="btn btn-primary" data-toggle="collapse" data-toggle-secondary="Close">
Open
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
JS:
$('[data-toggle-secondary]').each(function() {
var $toggle = $(this);
var originalText = $toggle.text();
var secondaryText = $toggle.data('toggle-secondary');
var $target = $($toggle.attr('href'));
$target.on('show.bs.collapse hide.bs.collapse', function() {
if ($toggle.text() == originalText) {
$toggle.text(secondaryText);
} else {
$toggle.text(originalText);
}
});
});
Examples:
$('[data-toggle-secondary]').each(function() {
var $toggle = $(this);
var originalText = $toggle.text();
var secondaryText = $toggle.data('toggle-secondary');
var $target = $($toggle.attr('href'));
$target.on('show.bs.collapse hide.bs.collapse', function() {
if ($toggle.text() == originalText) {
$toggle.text(secondaryText);
} else {
$toggle.text(originalText);
}
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link href="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet"/>
<script src="http://netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/js/bootstrap.min.js"></script>
<a href="#collapseExample" class="btn btn-primary" data-toggle="collapse" data-toggle-secondary="Close">
Open
</a>
<div class="collapse" id="collapseExample">
<div class="well">
...
</div>
</div>
JS Fiddle
Other benefits of this approach:
the code is DRY and reusable
each collapse button stays separate
you only need to put one change into the HTML: adding the data-toggle-secondary attribute
I guess you could look inside your downloaded code where exactly there is a + sign (but this might not be very easy).
What I'd do?
I'd find the class/id of the DOM elements that contain the + sign (suppose it's ".collapsible", and with Javascript (actually jQuery):
<script>
$(document).ready(function() {
var content=$(".collapsible").html().replace("+", "-");
$(".collapsible").html(content));
});
</script>
edit
Alright... Sorry I haven't looked at the bootstrap code... but I guess it works with something like slideToggle, or slideDown and slideUp... Imagine it's a slideToggle for the elements of class .collapsible, which reveal contents of some .info elements. Then:
$(".collapsible").click(function() {
var content=$(".collapsible").html();
if $(this).next().css("display") === "none") {
$(".collapsible").html(content.replace("+", "-"));
}
else $(".collapsible").html(content.replace("-", "+"));
});
This seems like the opposite thing to do, but since the actual animation runs in parallel, you will check css before animation, and that's why you need to check if it's visible (which will mean it will be hidden once the animation is complete) and then set the corresponding + or -.
Easier with inline coding
<button type="button" ng-click="showmore = (showmore !=null && showmore) ? false : true;" class="btn float-right" data-toggle="collapse" data-target="#moreoptions">
<span class="glyphicon" ng-class="showmore ? 'glyphicon-collapse-up': 'glyphicon-collapse-down'"></span>
{{ showmore !=null && showmore ? "Hide More Options" : "Show More Options" }}
</button>
<div id="moreoptions" class="collapse">Your Panel</div>
Some may take issue with changing the Bootstrap js (and perhaps validly so) but here is a two line approach to achieving this.
In bootstrap.js, look for the Collapse.prototype.show function and modify the this.$trigger call to add the html change as follows:
this.$trigger
.removeClass('collapsed')
.attr('aria-expanded', true)
.html('Collapse')
Likewise in the Collapse.prototype.hide function change it to
this.$trigger
.addClass('collapsed')
.attr('aria-expanded', false)
.html('Expand')
This will toggle the text between "Collapse" when everything is expanded and "Expand" when everything is collapsed.
Two lines. Done.
EDIT: longterm this won't work. bootstrap.js is part of a Nuget package so I don't think it was propogating my change to the server. As mentioned previously, not best practice anyway to edit bootstrap.js, so I implemented PSL's solution which worked great. Nonetheless, my solution will work locally if you need something quick just to try it out.
You do like this.
the function return the old text.
$('button').click(function(){
$(this).text(function(i,old){
return old=='Read More' ? 'Read Less' : 'Read More';
});
});
Applied and working in Bootstrap 5.0.1.
Using simple jQuery
jQuery('button').on( 'click', function(){
if(jQuery(this).hasClass('collapsed')){
jQuery(this).html('+');
} else {
jQuery(this).html('-');
}
});
You can also use font awesome or HTML instead of +/- signs.
I want to toggle visibility of multiple divs using knockout. Below is the rough idea of my problem -
<button>Button 1</button>
<button>Button 2</button>
<button>Button 3</button>
<div> Div 1 </div>
<div> Div 2 </div>
<div> Div 3 </div>
By default, 'Div 1' should be visible.
When I click individual buttons it should display only the related divs based on the buttons clicked.
I have gone through the Knockout live examples but not getting how to do this efficiently.
Please help!
The following will do a job for you. It's not ideal, but should give you a platform to work on.
First, everything in Knockout is tied to a view model. You want to be able to control the visibility of 3 divs, so here's a view model which might suit. Like I said, not perfect :)
var buttonVm = new function(){
var self = this;
// Flags for visibility
// Set first to true to cover your "first should be open" req
self.button1Visible = ko.observable(true);
self.button2Visible = ko.observable(false);
self.button3Visible = ko.observable(false);
self.toggle1 = function(){
self.button1Visible(!self.button1Visible());
}
self.toggle2 = function(){
self.button2Visible(!self.button2Visible());
}
self.toggle3 = function(){
self.button3Visible(!self.button3Visible());
}
}
You'll need to change your markup to:-
<!-- events here. When clicked call the referenced function -->
<button type="button" data-bind="click: toggle1">Button 1</button>
<button type="button" data-bind="click: toggle2">Button 2</button>
<button type="button" data-bind="click: toggle3">Button 3</button>
<!-- Visibility set here -->
<div data-bind="visible: button1Visible"> Div 1 </div>
<div data-bind="visible: button2Visible"> Div 2 </div>
<div data-bind="visible: button3Visible"> Div 3 </div>
Couple of things to note here. First, I've added the type attribute. Without it, the default behaviour of the button will be to try and submit your form.
Tying it all up:-
// Create view model
var vm = new buttonVm();
ko.applyBindings(vm);