Why isn't HTML working in Bootstrap v5 Popover? - html

When I add HTML into my Bootstrap v5 popover, it does not display the HTML in the popover. I believe that I have all of the correct options passed as described in the docs. Almost all of the results which I've gotten from searches to try and understand this issue deal with Bootstrap v4 and v3, so they aren't of use to me.
The popover all of the way on the right should have the switch affordance ("Toggle") appear below "Hello World" in the popover. The HTML code is present.
The code of the above tests centers around these 3 affordances:
<!-- Popover Works -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="Title" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World"></span>
<!-- HTML Switch Works -->
<div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch1'><label class='form-check-label' for='customSwitch1'>Toggle</label></div>
<!-- Popover with HTML Switch Does Not Work -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="Title" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World<br><div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch2'><label class='form-check-label' for='customSwitch2'>Toggle</label></div>"></span>
Have I missed something? Please let me know. Thank you!

Ok, reading and re-reading the Bootstrap v5 documentation's fine print finally led me to the solution.
Even though data-bs-sanitize="false" is a valid option that can be passed, per the documentation... the documentation also notes elsewhere that:
Note that for security reasons the sanitize, sanitizeFn, and allowList
options cannot be supplied using data attributes.
So... in order for the HTML to execute, because data-bs-sanitize="false" is ignored, one has to manually add non-default values to the Sanitizer whitelist which are used with the HTML code inside the popover. In my case, they were as follows:
var myDefaultAllowList = bootstrap.Tooltip.Default.allowList
// To allow elements
myDefaultAllowList.input = ['type', 'checked']
myDefaultAllowList.label = ['for']
And now... it's working as intended. A lesson in sanitization.

I'm not sure if you initialize the popovers correctly as data-bs-sanitize="false" should allow any html used.
You are adding elements that are not in the bootstrap.Tooltip.Default.allowList object. So to allow the elements and attributes used in your code the following config options will work to render the popover as intended:
$(document).ready(function() {
const myDefaultAllowList = bootstrap.Tooltip.Default.allowList
// To allow elements and attributes on elements
myDefaultAllowList.input = ['type', 'checked']
myDefaultAllowList.label = ['for']
$('[data-bs-toggle="popover"]').popover({
allowList: myDefaultAllowList,
html: true
})
/* or just disable the sanitzer
$('[data-bs-toggle="popover"]').popover({
sanitize: false
})
*/
})
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing2.css">
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing3.css">
<link rel="stylesheet" media="screen" href="https://hopspin.com/testing/testing.css">
<div class="container">
<div class="row" style="padding-top: 15em !important;">
<!-- Body -->
<div class="col-lg-12 content py-4 mb-2 mb-sm-0 pb-sm-1 no-sidebar-padding-override">
<div class="row row-panel-hs">
<div class="row section-container-bottom row-section-hs">
<table class="table keep-table-fixed no-margin-bottom entities-table">
<tbody>
<tr>
<td>Work alone:
</td>
<td>
<!-- Popover Works -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World" data-bs-original-title="Title" aria-label="Title"></span>
</td>
<td>
<!-- HTML Switch Works -->
<div class="form-check form-switch"><input type="checkbox" class="form-check-input" id="customSwitch1"><label class="form-check-label" for="customSwitch1">Toggle</label></div>
</td>
<td>
</td>
<td>
</td>
<td>Fail combined:
</td>
<td>
<!-- Popover with HTML Switch Does Not Work -->
<span class="fs-3 actions-link-style ai-more-horizontal" data-bs-container="body" data-bs-toggle="popover" data-bs-placement="top" title="" data-bs-sanitize="false" data-bs-html="true" data-bs-content="Hello World<br><div class='form-check form-switch'><input type='checkbox' class='form-check-input' id='customSwitch2'><label class='form-check-label' for='customSwitch2'>Toggle</label></div>"
data-bs-original-title="Title" aria-label="Title">...</span>
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap#5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
Note:
You need to do more work in order to preserve the checked state of the toggle. As it is, the toggle reverts back to the initial state when closed and re-opened. Fixing this would be a different question.

Related

Is there a way to parse html that include javascript in tags in ruby?

I am working on a web scraping problem in Ruby. I have seen multiple questions and answers related to this but in none I have seen HTML that include some JavaScript framework in it and I cannot figure out how to do it. I just want to select the HTML and return an array of objects. The following is my script and the HTML code. The HTML classes of the values like name, currency, balance are similar and the question of how can it be done?
content = document.css("div.acc-list").map do |parameters|
name = parameters.at_css("p.s3.bold.row.acDesc").text.strip, # argument?
currency = parameters.at_css(".row.ccy").text.strip, # argument?
balance = parameters.at_css(".row.acyOpeningBal").text.strip # argument?
Account.new name, currency, balance
end
pp content
These HTML paragraphs are inside multiple other classes which I think is due to the framework. However, they are inside a <div class = acc-list div>...</div> and I think I did correctly when I assigned "div.acc-list" to "content" variable.
<!-- HTML for name -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope icon-two-line-col" style="width: 17.3333rem;">
<div style="width: 17.333333333333332rem" class="first-cell cellText ng-scope">
<i bo-class="{'active':row.selected }" class="i-32 active icon i-circle-account"></i>
<div class="info-wrapper" style="">
<p class="s3 bold" bo-bind="row.acDesc">Name_value</p> # value
<a ui-sref="app.layout.ACCOUNTS.DETAILS.{ID}({id:'091601003439274'})" href="/Bank/accounts/details/BG37FINV91503006938102">
<span bo-bind="row.iban">BG37FINV91503006938102</span>
<i class="i-arrow-right-5x8"></i>
</a>
</div>
</div>
</td>
<!-- HTML for currency -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope" style="width: 4.4rem;">
<div style="width: 4.4rem" class="text-center cellText ng-scope">
<span bo-bind="row.ccy">EUR</span> # value
</div>
</td>
<!-- HTML for balance -->
<td bindonce="" ng-repeat="col in gridOptions.columns" sg-bind-html-compile="col.cellTemplate" bo-class="col.className" bo-style="{width: col.remWidth }"
class="ng-scope" style="width: 8.73333rem;">
<div style="width: 8.733333333333333rem" class="text-right cellText ng-scope">
<span bo-bind="row.acyAvlBal | sgCurrency">1 523.08</span> # value
</div>
</td>
Using CSS:
require 'nokogiri'
document = Nokogiri::HTML(<<EOT)
<div class="acc-list">
<!-- HTML for name -->
<td>
<div class="first-cell cellText ng-scope">
<div class="info-wrapper">
<!-- # value -->
<p class="s3 bold">Name_value</p>
</div>
</div>
</td>
<!-- HTML for currency -->
<td>
<div class="text-center cellText ng-scope">
<!-- # value -->
<span>EUR</span>
</div>
</td>
<!-- HTML for balance -->
<td>
<div class="text-right cellText ng-scope">
<!-- # value -->
<span>1 523.08</span>
</div>
</td>
</div>
EOT
Now that the DOM is loaded:
content = document.css('div.acc-list').map do |div|
name = div.at("p.s3.bold").text.strip # => "Name_value"
currency = div.at("div.text-center > span").text.strip # => "EUR"
balance = div.at("div.text-right > span").text.strip # => "1 523.08"
[ name, currency, balance ]
end
# => [["Name_value", "EUR", "1 523.08"]]
Your HTML sample has a lot of extraneous information that obscures the trees in this particular forest. I stripped it out because it wasn't useful. (And, when submitting a question you should automatically do that as part of simplifying the non-essential information so we can all focus on the actual problem.)
CSS doesn't care about parameters other than the node name, class and id. The class can chain the parameters in the definition of the class if you need that granular access, but often you can get away with a more general class selector; It just depends on the HTML.
Most XML and HTML parsing is basically the same tactic: Find an outer placeholder, look inside it and iterate grabbing the information needed. I can't demonstrate that completely because your example only has the outer div, but you can probably imagineer the necessary code to handle an inner loop.
at_css is almost equivalent to at, and Nokogiri is smart enough 99.9% of the time to determine whether a selector is CSS or XPath, so I tend toward using at because my fingers are lazy.

show a div on a button click using angularjs and HTML

I want to show a div on a button click using angularjs and HTML
I am learning angularJs.
I want to design a webpage in which on click of 'Add' button a div(EmployeeInfoDiv) is shown.
When I fill the textboxes in it and save it the data should reflect in table within the div(EmpDetTable).
Here is the HTML page code:
<body ng-app="MyApp">
<div ng-controller="EmpDetCtrl">
<div ng-model="EmpDetTable" class="container body" ng-hide="addEmp">
<label class="TabHeadings">Employee Details</label>
<div class="EmployeeInfo">
<table ng-model="Employee" border="1">
<thead><tr><th>Name</th><th>Designation</th><th>Salary</th></tr></thead>
<tbody>
<tr ng-repeat="emp in employees">
<td>{{emp.name}}</td>
<td>{{emp.desg}}</td>
</tr>
</tbody>
</table>
<button ng-model="AddEmp" ng-click="ShowAddEmployee()">Add</button>
</div>
</div>
<div ng-model="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;" ng-show="addEmp" >
<label class="TabHeadings" style="width:830px;">Employee Details</label>
<div class="EmployeeInfo">
<table>
<tr><td><label class="table_label">Name:</label></td><td><input type="text" ng-model="name" class="textbox"/></td>
<td><label class="table_label">Designation:</label></td><td><input type="text" ng-model="desg" class="textbox"/></td>
</tr>
</table>
<div ng-model="botton_container">
<button ng-model="save" class="save_buttons" ng-click="SaveData();">Save</button>
<button ng-model="cancel" class="cancel_buttons" ng-click="ShowViewEmployee();">Cancel</button>
</div>
</div>
</div>
</div>
</body>
Controller Code:
function EmpDetCtrl($scope) {
$scope.employees = [
{ name: 'A',desg: 'C'}
];
$scope.addNew = function () {
$scope.employees.push({
name: $scope.name,desg: $scope.desg
});
}
$scope.ShowAddEmployee= function () {
return $scope.EmployeeInfoDiv=true;
}
$scope.ShowViewEmployee=function () {
return $scope.EmployeeInfoDiv = false;
}
}
I don't know whether this code is correct or not.
I just tried it.
Can anyone please help me out.
Thanks in advance.
Your ng-click is updating the EmployeeInfoDiv variable. So, reference that in ng-show and ng-hide:
<div ng-hide="EmployeeInfoDiv" class="container body">
...
<div ng-show="EmployeeInfoDiv" class="popover right EmployeeInfo" style="width:1500px;">
You do not need ng-model in the div to make that work.
ng-model="EmployeeInfoDiv"
Update
A few more issues. Most important is that (at least in the code posted) MyApp isn't defined anywhere, and your controller is defined on the global scope. So you need to change your html to:
<div ng-app>
<div ng-controller="EmpDetCtrl">
...
However, note that this is not the recommended method. See the angular documentation for controllers.
Here is a working demo: http://jsfiddle.net/wittwerj/xrB77/

Ember switching between templates in {output}

I'm building my first ember app which has CRUD properties. Part of my index.html code looks like this-
<script type = "text/x-handlebars" id = "settings">
<table>
{{#each setting in controller}}
<tr>
<td>
<h2>{{setting.name}}</h2>
</td>
</tr>
<tr>
<td>{{#link-to "setting" setting}}More Details..{{/link-to}}</td>
</tr>
{{/each}}
</table>
{{outlet}}
</script>
<script type = "text/x-handlebars" id = "setting">
{{#if deleteMode}}
<div class="confirm-box">
<h4>Really?</h4>
<button {{action "confirmDelete"}}> yes </button>
<button {{action "cancelDelete"}}> no </button>
</div>
{{/if}}
<div class="col-xs-6">
<h4>{{name}}</h4>
<h4>{{address_line1}}</h4>
<h4>{{address_line2}}</h4>
<h4>{{address_city}}</h4>
<h4>{{address_state}}</h4>
<h4>{{address_zip}}</h4>
<h4>{{address_country}}</h4>
<button {{action "edit"}}>Edit</button>
</div>
<div class = "col-xs-6">
{{outlet}}
</div>
</script>
<script type = "text/x-handlebars" id = "setting/edit">
<div class="input-group">
<h5>Name:</h5>
{{input value=name}}
<h5>Address:</h5>
{{input value=address_line1}}
{{input value=address_line2}}
<h5>City:</h5>
{{input value=address_city}}
<h5>State:</h5>
{{input value=address_state}}
<h5>ZIP:</h5>
{{input value=address_zip}}
<h5>Country:</h5>
{{input value=address_country}}
<button {{action "save"}}> Save </button>
</div>
</script>
When I click on the edit button in the setting template, the setting/edit template with an edit form opens in the {outlet}of the setting template. I'm wondering how I would go about getting the setting/edit template to replace the setting template in the settings template's {outlet} so that they don't appear on the same page? New to ember dev and somewhat confused, any help appreciated.
Ember always routes to leaf route. when you are on settingRoute(means only setting template is rendered) in reality your leaf route is settingIndexRoute and in {outlet} of setting route setting/index template is rendered,which is generated by ember when it found out that you have not specified index template. so when you click on edit, setting/edit will replace setting/index template. so I would suggest that you move your setting template's content in setting/index.

jQuery deleting multiple items instead of just one

I have a table which has buttons when you hover on its rows. There is a delete button that calls a custom confirm dialog; this basically just accepts the callback function that it will perform when the delete is confirmed.
The HTML is as follows:
<!DOCTYPE html>
<html>
<head>
<title>Home</title>
<link rel="stylesheet" type="text/css" href="css/styles.css" />
</head>
<body>
<section>
<table>
<thead>
<tr>
<th>Name</th>
<th>Age</th>
</tr>
</thead>
<tbody>
<tr>
<td>Doe, Jane</td>
<td>
<div>
25
<span class="actions">
<input type="button" value="Edit" class="btn-edit" />
<input type="button" value="Delete" class="btn-delete" />
</span>
</div>
</td>
</tr>
<tr>
<td>Doe, John</td>
<td>
<div>
35
<span class="actions">
<input type="button" value="Edit" class="btn-edit" />
<input type="button" value="Delete" class="btn-delete" />
</span>
</div>
</td>
</tr>
<tr>
<td>Smith, John</td>
<td>
<div>
30
<span class="actions">
<input type="button" value="Edit" class="btn-edit" />
<input type="button" value="Delete" class="btn-delete" />
</span>
</div>
</td>
</tr>
</tbody>
</table>
</section>
<!-- popups -->
<div id="confirm-delete" class="popup confirm-dialog">
<h3>Delete</h3>
<div class="popup-content">
<p class="confirm-message">Are you sure you want to delete this item?</p>
<div class="buttons">
<input id="btn-delete-confirm" type="button" class="button btn-confirm" value="Delete" />
<input id="btn-delete-cancel" type="button" class="button btn-close-popup" value="Cancel" />
</div>
</div>
</div>
<script type="text/javascript" src="js/jquery.js"></script>
<script type="text/javascript" src="js/home.js"></script>
</body>
</html>
JavaScript codes:
$(document).ready(function(){
var btnDelete = $('.btn-delete');
btnDelete.on('click', function(e){
var popUpId = 'confirm-delete',
btn = $(e.currentTarget),
item = $(e.currentTarget).closest('tr'),
header = 'Delete Item',
message = 'Are you sure you want to delete ' + item.find('.name').text() + '?';
customConfirm(popUpId, header, message, function(){ deleteItem(item); });
});
});
function customConfirm(id, header, message, callback){
var popUp = $('#' + id),
btnConfirm = popUp.find('.btn-confirm');
popUp.find('h3').html(header);
popUp.find('.confirm-message').html(message);
popUp.append('<div class="mask"></div>');
popUp.show();
btnConfirm.on('click', {popUpId: id, callBack: callback}, confirmClick);
}
function confirmClick(e){
e.data.callBack();
closePopUp(e);
}
This works fine when I click on the delete button for one item and confirm its deletion. But when I click on the delete button of an item and then cancel it, click on another item's delete button and confirm, both items are deleted. It happens everytime I cancel the deletion of an item. So if I cancel deleting three items and confirm the deletion of the next item, four items will be deleted.
Help on this would be greatly appreciated.
Thank you :)
PS
Here's the jsfiddle link :)
What's happening is, the click handlers from previous showings of the dialog are still attached to its "Confirm" button. When you click the button, all the previous handlers are firing, and thus all the previous deletions are being confirmed.
You need to get rid of the previous handlers, either when the dialog is dismissed or as part of showing the dialog. It's probably simpler, i think, to get rid of it when showing the form; you could simply say .off('click') before attaching the new click handler:
btnConfirm
.off('click')
.on('click', {popUpId: id, callBack: callback}, confirmClick);
I think this should work..
i have modified you js code a bit
http://jsfiddle.net/VKKpe/
changed this
btnConfirm = popUp.find('.btn-confirm');
to
btnConfirm = $('#btn-delete-confirm');
I've encountered this problem before and it's not something I would have really thought about, and I'm super glad that I have the opportunity to help someone with it:
binding with .on() adds a binding, doesn't replace it.. So it will execute everything you have bound to it in the past.
Simple Solution: btnConfirm.unbind('click'); right before binding.
Working example: http://jsfiddle.net/asifrc/VXsEB/8/

Toggle specific div and change img jquery

I have a problem with toggle on this list:
<div id="list">
<div id="segment"> // <--- when clicked, toggle segm_content and opener
<div id="opener">
<img src="images/open.png" /> // changes when toggled
</div>
<div id="segm_content">
// content to hide/show
</div>
</div>
<div id="segment"> // <--- when clicked, toggle segm_content and opener
<div id="opener">
<img src="images/open.png" /> // changes when toggled
</div>
<div id="segm_content">
// content to hide/show
</div>
</div>
... //and so on
</div>
I want clicked "#segment" to toggle child *"#segm_content"* and change img in "#opener".
I made it working with this code:
$('#segment').toggle(function() {
$('#opener').html('<img src="images/open.png"/>');
$('#segm_content').hide(500);
}, function() {
$('#opener').html('<img src="images/close.png"/>');
$('#segm_content').show(500);
});
But I can't figure out how to do it only for one "#segment" at a time.
This code toggles everything, which I don't want.
I am stuck at this point, any suggestions please?
Many thanks!
I really wouldn't recommend this. The point of an id is to reference a unique element. If you want to select multiple elements, you should define a class instead and have jQuery call that. Multiple ids is invalid HTML. But you could, per sé, do this by using changing your jQuery code to the following.
(Here is my jsFiddle: http://jsfiddle.net/KzVmK/)
$('[id="segment"]').toggle(
function(){
$(this).find('[id="opener"]').html('<img src="open.png" alt="Close" />');
$(this).find('[id="segm_content"]').hide(500);
},
function(){
$(this).find('[id="opener"]').html('<img src="close.png" alt="Open" />');
$(this).find('[id="segm_content"]').show(500);
}
);​
Again, let me stress again that this is a bad idea, because you will not have unique id selectors in your document. This is really bad practice. There are times when you will want to select an individual element in the DOM and this will make that next to impossible. I would highly advise you to define a class for the elements (you can still define CSS classes, e.g. <div class="opener my-class" /> or <div class="segm_content my-class" />).
(Also, a helpful tip with this code: rather than populating the HTML elements with the same image that is also in the jQuery code [which is redundant], leave the <div id="opener" /> elements empty. Then, right after you define the toggle function, run the click event, like so: $('[id="$segment"]').toggle(...).click();
http://jsfiddle.net/XPXBv/).
General Theme Settings
Back-Ground Color
Text Color
<div class="Settings" id="GTSettings">
<h3 class="SettingsTitle"><a class="toggle" ><img src="${appThemePath}/images/toggle-collapse-light.gif" alt="" /></a>Content Theme Settings</h3>
<div class="options">
<table>
<tr>
<td><h4>Back-Ground Color</h4></td>
<td><input type="text" id="body-backGroundColor" class="themeselector" readonly="readonly"></td>
</tr>
<tr>
<td><h4>Text Color</h4></td>
<td><input type="text" id="body-fontColor" class="themeselector" readonly="readonly"></td>
</tr>
</table>
</div>
</div>
$(document).ready(function(){
$(".options").hide();
$(".SettingsTitle").click(function(e) {
var appThemePath = $("#appThemePath").text();
var closeMenuImg = appThemePath+'/images/toggle-collapse-light.gif';
var openMenuImg = appThemePath+'/images/toggle-collapse-dark.gif';
var elem = $(this).next('.options');
$('.options').not(elem).hide('fast');
$('.SettingsTitle').not($(this)).parent().children("h3").children("a.toggle").children("img").attr('src', closeMenuImg);
elem.toggle('fast');
var targetImg = $(this).parent().children("h3").children("a.toggle").children("img").attr('src') === closeMenuImg ? openMenuImg : closeMenuImg;
$(this).parent().children("h3").children("a.toggle").children("img").attr('src', targetImg);
});
});