D3.js - Append objects outside of the selection - javascript

How do you select a div and then create a new div right below it?
function addContainer(row)
{
row++
d3.select('body')
.append('div')
.attr('class', `#container${row}`)
.attr("onclick", `addContainer(${row})`)
.text("Container " + `${row}`)
}
<div id="container0" onclick="addContainer(0)" >Container 0</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
Everything works fine except for the part that I want the added div to be exactly below the div I clicked on and not at the very bottom. So my idea was that instead of selecting the body I select the id of the div I clicked on and then append a new div. However this adds a div within the div and not below it.
So how do I append it outside of the selection or is there a better way to do it?

Here's one approach to do that: Use d3.insert.
And due to the this issues while binding events onclick on HTML elements, I've moved the event binding and handling to the JS section.
This is how I'm appending a <div> as the next sibling to the selected (clicked) element:
this.parentNode.insertBefore(this.cloneNode(deep), this.nextSibling)
Code:
d3.select('#container0').on('click', addContainer);
function addContainer(row)
{
if(!row) row = 0;
row++
d3.select(this).select(function () {
return this.parentNode.insertBefore(document.createElement('div'), this.nextSibling);
})
.attr('class', `#container${row}`)
.on('click', function () { addContainer.call(this, row); })
.text("Container " + `${row}`)
}
<div id="container0">Container 0</div>
<script src="https://d3js.org/d3.v5.min.js"></script>
Notice the addContainer.call(this, row) to bind the this used while selection.
Hope this helps. And about the container number, it adds +1 to the bound element's number. If you want the container number to keep on increasing, just declare the row outside and take it out while calling the function (as this'll help the divs to have unique IDs). And do check the DOM if it's inserting the elements correctly.

Related

How to remove style attribute added with jquery

I am using a devExpress table with some custom requirements.
(UPDATE)
Took a break from this for a day and went back and did it properly using React Styling! Thanks for suggestions
In the screenshot I have certain cells disabled. However the user wants all cells to look disabled other that the row selected.
Using this
window
.$("td")
.not(document.getElementById(this.state.selection[0]))
.not(document.getElementsByClassName(this.state.selection[0]))
.not("td:first-child")
.not(window.$("td:contains('iPlay')"))
.not(window.$("td:contains('iLOE')"))
.not(window.$("td:contains('iInvest')"))
.not(window.$("td:contains('SPACER')"))
.not(window.$("td:contains('$MM')"))
.not(window.$("td:contains('$/BOE')"))
.attr("style", "color:#868a8f");
window
.$("td > div > div > div > input")
.not(document.getElementsByClassName(this.state.selection[0]))
.attr("style", "color:#868a8f");
I managed to achieve my desired result on page load
My problem is when I select a new row I cannot remove that color I applied before when it was not selected. I am trying to use "has" to find the selected row and change the color back to inherit or completely remove the style attribute.
window
.$("td")
.has(document.getElementById(this.state.selection[0]))
.has(document.getElementsByClassName(this.state.selection[0]))
.not("td:first-child")
.not(window.$("td:contains('iPlay')"))
.not(window.$("td:contains('iLOE')"))
.not(window.$("td:contains('iInvest')"))
.not(window.$("td:contains('SPACER')"))
.not(window.$("td:contains('$MM')"))
.not(window.$("td:contains('$/BOE')"))
.attr("style", "color:inherit");
window
.$("td > div > div > div > input")
.has(document.getElementsByClassName(this.state.selection[0]))
.attr("style", "color:inherit");
If it helps I do have the ids of the rows that are NOT selected.
I tried to do something with that but did not have any luck
const otherRows = ExpensesUtils.ROW_PROPS.filter(x => x !== this.state.selection[0]);
for (let i = 0; i < otherRows.length; i += 1) {
window
.$("td")
.has(document.getElementById(otherRows[i]))
.has(document.getElementsByClassName(otherRows[i]))
.attr("style", "color:inherit");
window
.$("td > div > div > div > input")
.has(document.getElementById(otherRows[i]))
.has(document.getElementsByClassName(otherRows[i]))
.attr("style", "color:inherit");
}
link to HTML
Table HTML
this.state.selection[0] is the selected rowId from the list below
I have applied the the rowIds to classes in the nested components. I could not figure out another way to access them.
const ROW_PROPS = [
"leaseAndWellExpense",
"leaseAndWellExpenseBoe",
"iloeLeaseAndWellExpense",
"iloeLeaseAndWellExpenseBoe",
"gnaLeaseAndWell",
"gnaLeaseAndWellBoe",
"transportation",
"transportationBoe",
"divisionGnA",
"divisionGnABoe",
"gatheringProcessing",
"gatheringProcessingBoe",
"hqGnA",
"hqGnABoe",
"interestExpense",
"interestExpenseBoe",
"netProdBoe",
"leaseImpairments",
"leaseImpairmentsBoe",
"ddaProducing",
"ddaProducingBoe",
"iInvestDdaProducing",
"iInvestDdaProducingBoe",
"ddaGatheringProcessing",
"ddaGatheringProcessingBoe",
"iInvestDdaGatheringProcessing",
"iInvestDdaGatheringProcessingBoe",
"marketingCosts",
"otherIncomeExpense",
"otherIncomeExpenseBoe",
"otherRevenue",
"incomeTaxProvision",
"incomeTaxProvisionBoe",
"severanceTaxes",
"severanceTaxesPercent",
"currentTaxes",
"currentTaxesRate",
"netWellHeadRevenue",
];
The easiest way of doing this is by creating a CSS rule's stylesheet.
In that stylesheet, you should define 2 classes.
Let's suppose 1 for your desired CSS rules and the other for the default/none rules.
I am just showing you the simplest version of doing this thing but with another aspect.
$('#b1').on('click', function() {
$('.c1').removeClass('c1');
$(this).addClass('c2');
});
.c1 {
color: red;
}
.c2 {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="b1">Change</button>
<p class="c1">This is a Test Line.</p>
The easiest way is
$('#idName').on('click', function()
{
$('.className').removeClass('removeClassName');
$(this).addClass('addClassName');
});
The code above means that when a button with the id of IdName is clicked, the element with className will be removing the class of removeClassName, and adding the class of addClassName.
For further clarification you can have a look at Removing CSS Using JQuery Documentation
There is another way by which you can achieve it.
Instead of playing with style attribute, since it takes the highest specificity so somewhere it might create an issue.
Instead of that you can use toggleClass. First add your default styling to table, whenever you click any row you can make use of toggle class
Toggle Class Example
Example.
$("td > div > div > div").click(function(){
$("input").toggleClass("main");
})

How to delete current element if previous element is empty in jquery?

I want to delete element with class "tehnicneinfo" but only if the element I'm checking ( with class "h2size") has no child. I have a bunch of those elements, generated by a plugin and I want to delete only the ones that have the next element without child. I wrote jquery code, but it delets all of my elements, not only the ones that have the next element without child. Here is my jquery code:
$('.news .h2size > div').each(function() {
var ul = $(this).find('ul');
if(!ul.length) $(this).remove();
var h1 = $('.news').find('.tehnicneinfo');
var h2size = $('.news').find('.h2size');
if(h2size.prev().is(':empty'))
{
h1.remove();
}
});
this code is inside $(document).ready(function(). Can you tell me what I'm doing wrong? The code is for something else also, so I'm having truble only from var h1 = $('.news').find('.tehnicneinfo'); this line on. Thanks in advance!
Html:
<div class="news">
<h1 class="tehnicneinfo">xxx</h1>
<div class="h2size">
<div id="xyxyxy">
.......
</div>
</div>
<h1 class="tehnicneinfo">yyy</h1>
<div class="h2size"></div>
....
</div>
That's the html, only that there is like 20 more lines that are the same, but with different values (not yyy and xxx). I would need to delete all 'yyy' (they are not all with same value).
You can use filter to filter the ones you want to remove then remove them
"I want to delete only the ones that have the next element without child"
$('.tehnicneinfo').filter(function(){
return !$(this).next().children().length;
// only ones with next sibling with no children
}).remove();
JSFIDDLE

How do i select all nodes and apply a css style to all of them, D3

I have an array of nodes. I wish to click a HTML button and change the style of all those nodes to that one style.
(For example: when i search for a node or click to select, i want to click the 'clear' button so everything resets)
Surely there's an easy answer to this but i cant seem to get it
.node.selectedNode {
width:50px;
height:50px;
stroke-width: 3px;
stroke: #f00;
}
.node.unselectedNode {
}
above is the CSS that i wish to alternate between
To add or remove a CSS class, you can use the selection.classed function:
// Select all elements with the node class
d3.selectAll(".node")
.classed("selectedNode", true) // Add the selectedNode class to the selection
.classed("unselectedNode", false); // Remove the unselectedNode class to the selection
The selection.on function can be used to listen for a click on a button, for example, for the clear button functionality, if you have button like this:
<button id="reset">Clear</button>
Then you can set the classed appropriately:
var unselectAllNodes = function () {
d3.selectAll(".node")
.classed("selectedNode", false)
.classed("unselectedNode", true);
};
// Call the unselectAllNodes function when this button is clicked
d3.select("button#reset")
.on('click', unselectAllNodes);
lets say your node is rect, you can use the .on('click') to
button on click ==> set_variable a scope higher
==> call D3 function to rerender
var set_variable;
$('#button').on('click', function () {
if(something) {set_variable="classA";}
else {set_variable="classB";}
D3Function();
});
D3Function ==> ...
canvas.selectAll("rect").data(scope.input).enter()
.append("rect").call(yAxis)
.attr("class", function(d, i) { return set_variable; })
.on("click", function(d, i){
//d is the document, i the index
});
....
$('#reset').on('click', function () {
set_variable="";
D3Function();
});
You can add classes to nodes with code like this: .attr("class", "link").
For more context, you can see the example. This particular example uses svg.selectAll() to select all elements with the link class, which may or may not work in your case. If you need more complex selections, the relevant documentation is here
As an alternative, the .attr method supports using a function to take action based on the data for a selected node. You can find more information in the documentation

onclick event won't fire when there is more than one dynamically added button

So I have EDIT and REMOVE buttons that are dynamically added for each data node (a "poll") in a Firebase database. I have a function which assigns onclick listeners to these with jQuery, but oddly, the event only fires when there just happens to be a single node, and hence a single pair of EDIT/REMOVE buttons. When there are multiple nodes and multiple pairs of buttons, none will fire. Here's the javascript where the events are added to the buttons...
function displayCurrentPollsForEditing(pollsRef)
{
var tbl = createTable();
var th = ('<th>Polls</th>');
$(th).attr('colspan', '3');
$(th).appendTo($(tbl).children('thead'));
pollsRef.once('value', function(pollsSnapshot) {
pollsSnapshot.forEach(function(pollsChild) {
var type = pollsChild.name();
// If this is true if means we have a poll node
if ($.trim(type) !== "NumPolls")
{
// Create variables
var pollRef = pollsRef.child(type);
var pollName = pollsChild.val().Name;
var btnEditPoll = $('<button>EDIT</button>');
var btnRemovePoll = $('<button>REMOVE</button>');
var tr = $('<tr></tr>');
var voterColumn = $('<td></td>');
var editColumn = $('<td></td>');
var rmvColumn = $('<td></td>');
// Append text and set attributes and listeners
$(voterColumn).text(pollName);
$(voterColumn).attr('width', '300px');
$(btnEditPoll).attr({
'class': 'formee-table-button',
'font-size': '1.0em'
});
$(btnRemovePoll).attr({
'class': 'formee-table-remove-button',
'font-size': '1.0em'
});
$(btnEditPoll).appendTo($(editColumn));
$(btnRemovePoll).appendTo($(rmvColumn));
// Append to row and row to table body
$(tr).append(voterColumn).append(editColumn).append(rmvColumn);
$(tr).appendTo($(tbl).children('tbody'));
// Append table to div to be displayed
$('div#divEditPoll fieldset#selectPoll div#appendPolls').empty();
$(tbl).appendTo('div#divEditPoll fieldset#selectPoll div#appendPolls');
$(btnEditPoll).click(function() {
displayPollEditOptions(pollRef);
return false;
});
$(btnRemovePoll).click(function() {
deletePoll($(this), pollsRef);
return false;
});
}
});
});
}
The markup would be something like the following...
<div id="divEditPoll">
<form class="formee" action="">
<fieldset id="selectPoll">
<legend>SELECT A POLL</legend>
<div class="formee-msg-success">
</div>
<div class="grid-12-12" id="appendPolls">
</div>
</fieldset>
</div>
EDIT - So I've switched some lines around and now I don't set the click() events until the buttons are appended to the document, so the button elements are definitely in the DOM when the click events are attached. So could this issue result from not setting id's for these buttons? That seems strange to me, since I'm using variable references rather than ids to attach the events.
There are two things I would check for.
First, make sure you don't have two elements with the same id. If you do, jquery may only bind to the first, or not bind at all.
Second, make sure the element is added to the dom before jquery attempts to bind the click event. If the code is running asynchronously, which can easily happen if you're using ajax, then you may be trying to bind the event before creating the element. Jquery would fail to find the element then give up silently.
you should use .on() for dynamically added button

How to Reduce Size of This jQuery Script and Make it More Flexible?

I just created script that shows/hides (toggles) block of HTML. There are four buttons that each can toggle its HTML block. When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button... it hides that HTML block and shows new one.
Here is what I have at the moment:
$('.btn_add_event').click( function() {
$('.block_link, .block_photos, .block_videos').hide();
$('.block_event').toggle();
});
$('.btn_add_link').click( function() {
$('.block_event, .block_photos, .block_videos').hide();
$('.block_link').toggle();
});
$('.btn_add_photos').click( function() {
$('.block_event, .block_link, .block_videos').hide();
$('.block_photos').toggle();
});
$('.btn_add_videos').click( function() {
$('.block_event, .block_link, .block_photos').hide();
$('.block_videos').toggle();
});
Any ideas how to reduce code size? Also, this script isn't very flexible. Imagine to add two new buttons and blocks.
like Sam said, I would use a class that all the blocks share, so you never have to alter that code. Secondly, you can try 'traversing' to the closest block, therefore avoiding it's name. That approach is better than hard coding each specific block, but if the html dom tree changes you will need to refactor. Last, but best, you can pass in the class name desired block as a variable to the function. Below is something you can copy paste that is close to what you started with.
$('.myAddButtonClass').click( function() {
$('.mySharedBlockClass').filter(':visible').hide();
//find a good way to 'traverse' to your desired block, or name it specifically for now.
//$(this).closest(".mySharedBlockClass").show() complete guess
$('.specificBlockClass').show();
});
I kept reading this "When any HTML block is opened, but user has been clicked on other button than that HTML block's associated button" thinking that my eyes were failing me when Its just bad English.
If you want to make it more dynamic, what you can do is add a common class keyword. Then
when the click event is raise. You can have it loop though all the classes that have the
keyword and have it hide them all (except the current one that was clicked) and then show the current one by using the 'this' keyword.
you can refer below link,
http://chandreshmaheshwari.wordpress.com/2011/05/24/show-hide-div-content-using-jquery/
call function showSlidingDiv() onclick event and pass your button class dynamically.
This may be useful.
Thanks.
try this
$('input[type=button]').click( function() {
$('div[class^=block]').hide(); // I resumed html block is div
$(this).toggle();
});
Unfortunatly I couldn't test it, but if I can remember right following should work:
function toogleFunc(clickObject, toogleTarget, hideTarget)
{
$(clickObject).click(function()
{
$(hideTarget).hide();
$(toogleTarget).toggle();
});
}
And the call:
toogleFunc(
".btn_add_videos",
".block_videos",
".block_event, .block_link, .block_photos"
);
and so far
Assuming the buttons will only have one class each, something like this ought to work.
var classNames = [ 'btn_add_event', 'block_link', 'block_photos', 'block_videos' ];
var all = '.' + classNames.join(', .'); // generate a jquery format string for selection
$(all).click( function() {
var j = classNames.length;
while(j--){
if( this.className === classNames[j] ){
var others = classNames.splice(j, 1); // should leave all classes but the one on this button
$('.' + others.join(', .')).hide();
$('.' + classNames[j]).toggle();
}
}
}
All the buttons have the same handler. When the handler fires, it checks the sender for one of the classes in the list. If a class is found, it generates a jquery selection string from the remaining classes and hides them, and toggles the one found. You may have to do some checking to make sure the strings are generating correctly.
It depends by how your HTML is structured.
Supposing you've something like this
<div class="area">
<div class="one"></div>
<div class="two"></div>
<div class="three"></div>
</div>
...
<div class="sender">
<a class="one"></a>
<a class="two"></a>
<a class="three"></a>
</div>
You have a class shared by the sender and the target.
Your js would be like this:
$('.sender > a').click(function() {
var target = $(this).attr('class');
$('.area > .' + target).show().siblings().hide();
});
You show your real target and hide its siblings, which aren't needed.
If you put the class postfixes in an array, you can easily make this code more dynamic. This code assumed that it doesn't matter in which order toggle or hide are called. If it does matter, you can just remember the right classname inside the (inner) loop, and toggle that class after the loop.
The advantage to this approach is that you can extend the array with an exta class without needing to modifying the rest of the code.
var classes = new Array('videos', 'event', 'link', 'photos');
for (var i = 0; i < classes.length; ++i)
{
$('.btn_add_' + classes[i]).click(
function()
{
for (var j = 0; j < classes.length; ++j)
{
if (this.hasClass('btn_add_' + classes[j]))
{
$('.block_' + classes[j]).toggle();
}
else
{
$('.block_' + classes[j]).hide();
}
}
});
}
You could make this code more elegant by not assigning those elements classes like btn_add_event, but give them two classes: btn_add and event, or even resort to giving them id's. My solution is based on your description of your current html.
Here is what I think is a nice flexible and performant function. It assumes you can contain your links and html blocks in a parent, but otherwise it uses closures to precalculate the elements involved, so a click is super-fast.
<html>
<head>
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js" ></script>
<script type="text/javascript">
// Enables show/hide functionality on click.
// The elements within 'container' matching the selector 'blocks' are hidden
// When elements within 'container' matching the selector 'clicker' are clicked
// their attribute with the name 'clickerAttr' is appended to the selector
// 'subject' to identify a target, usually one of the 'blocks'. All blocks
// except the target are hidden. The target is shown.
//
// Change clickerAttr from 'linkTarget' to 'id' if you want XHTML compliance
//
// container: grouping of related elements for which to enable this functionality
// clicker: selector to element type that when clicked triggers the show/hide functionality
// clickerAttr: name of the DOM attribute that will be used to adapt the 'subject' selector
// blocks: selector to the html blocks that will be shown or hidden when the clicker is clicked
// subject: root of the selector to be used to identify the one html block to be shown
//
function initToggle(container,clicker,clickerAttr,blocks,subject) {
$(container).each(
function(idx,instance) {
var containerElement = $(instance);
var containedBlocks = containerElement.find(blocks);
containerElement.find(clicker).each(function(idxC, instanceClicker) {
var tgtE = containerElement.find(subject+instanceClicker.getAttribute(clickerAttr));
var clickerBlocks = containedBlocks.not(tgtE);
$(instanceClicker).click(function(event) {
clickerBlocks.hide();
tgtE.toggle();
});
});
// initially cleared
containedBlocks.hide();
}
);
}
$(function() {
initToggle('.toggle','a.link','linkTarget','div.block','div.');
});
</script>
</head>
<body>
Example HTML block toggle:
<div class="toggle">
a <br />
b <br />
c <br />
<div class="A block"> A </div>
<div class="B block"> B </div>
<div class="C block"> C </div>
</div> <!-- toggle -->
This next one is not enabled, to show scoping.
<div class="toggle2">
a <br />
<div class="A block">A</div>
</div> <!-- toggle2 -->
This next one is enabled, to show use in multiple positions on a page, such as in a portlet library.
<div class="toggle">
a <br />
<div class="A block">A</div>
</div> <!-- toggle (2) -->
</body>
</html>

Categories