How can I set a data attribute on an element? [duplicate] - javascript

This question already has answers here:
How do I dynamically set HTML5 data- attributes using react?
(2 answers)
Closed 2 years ago.
Silly question but...
I have a div which looks like this:
<div class="container">
<div class="abc" />
<div class="abc" />
<div>
Can I set the value for the div.abc like this <div class="abc" value={2}></div>? And then use js to calculate the total by iterate through the child of div.container?

EDIT: as isherwood mentioned in the comments. This answer is just javascript solution and not specific for reactjs as I missed that question had reactjs tag. See recommended answer How do I dynamically set HTML5 data- attributes using react? for better solution!
You probably looking for html data attribute
<div class="container">
<div data-your-prop="1" />
<div data-your-prop="1.5" />
<div data-your-prop="2" />
<div>
e.g. 1
const divs = document.querySelectorAll('.container div[data-your-prop]')
divs.forEach(function (el, index) {
console.log(el.dataset.yourProp)
})
e.g. 2
const divs = document.querySelectorAll('.container div[data-your-prop]')
let totalInt = 0
let totalFloat = 0
divs.forEach(function (el, index) {
totalInt += parseInt(el.dataset.yourProp)
totalFloat += parseFloat(el.dataset.yourProp)
});
console.log(totalInt)
console.log(totalFloat.toFixed(2))
// yields
//
// 4
// "4.50"
note
(data-) attribute names are attached to el.dataset and names defined in dom are normalized to be valid json property.

Related

Excluding inner tags from string using Regex

I have the following text:
If there would be more <div>matches<div>in</div> string</div>, you will merge them to one
How do I make a JS regex that will produce the following text?
If there would be more <div>matches in string</div>, you will merge them to one
As you can see, the additional <div> tag has been removed.
I would use a DOMParser to parseFromString into the more fluent HTMLDocument interface to solve this problem. You are not going to solve it well with regex.
const htmlDocument = new DOMParser().parseFromString("this <div>has <div>nested</div> divs</div>");
htmlDocument.body.childNodes; // NodeList(2): [ #text, div ]
From there, the algorithm depends on exactly what you want to do. Solving the problem exactly as you described to us isn't too tricky: recursively walk the DOM tree; remember whether you've seen a tag yet; if so, exclude the node and merge its children into the parent's children.
In code:
const simpleExampleHtml = `<div>Hello, this is <p>a paragraph</p> and <div>some <div><div><div>very deeply</div></div> nested</div> divs</div> that should be eliminated</div>`
// Parse into an HTML document
const doc = new DOMParser().parseFromString(exampleHtml, "text/html").body;
// Process a node, removing any tags that have already been seen
const processNode = (node, seenTags = []) => {
// If this is a text node, return it
if (node.nodeName === "#text") {
return node.cloneNode()
}
// If this node has been seen, return its children
if (seenTags.includes(node.tagName)) {
// flatMap flattens, in case the same node is repeatedly nested
// note that this is a newer JS feature and lacks IE11 support: https://caniuse.com/?search=flatMap
return Array.from(node.childNodes).flatMap(child => processNode(child, seenTags))
}
// If this node has not been seen, process its children and return it
const newChildren = Array.from(node.childNodes).flatMap(child => processNode(child, [...seenTags, node.tagName]))
// Clone the node so we don't mutate the original
const newNode = node.cloneNode()
// We can't directly assign to node.childNodes - append every child instead
newChildren.forEach(child => newNode.appendChild(child))
return newNode
}
// resultBody is an HTML <body> Node with the desired result as its childNodes
const resultBody = processNode(doc);
const resultText = resultBody.innerHTML
// <div>Hello, this is <p>a paragraph</p> and some very deeply nested divs that should be eliminated</div>
But make sure you know EXACTLY what you want to do!
There's lots of potential complications you could face with data that's more complex than your example. Here are some examples where the simple approach may not give you the desired result.
<!-- nodes where nested identical children are meaningful -->
<ul>
<li>Nested list below</li>
<li>
<ul>
<li>Nested list item</li>
</ul>
</li>
</ul>
<!-- nested nodes with classes or IDs -->
<span>A span with <span class="some-class">nested spans <span id="DeeplyNested" class="another-class>with classes and IDs</span></span></span>
<!-- places where divs are essential to the layout -->
<div class="form-container">
<form>
<div class="form-row">
<label for="username">Username</label>
<input type="text" name="username" />
</div>
<div class="form-row"
<label for="password">Password</label>
<input type="text" name="password" />
</div>
</form>
</div>
Simple approach without using Regex by using p element of html and get its first div content as innerText(exclude any html tags) and affect it to p, finally get content but this time with innerHTML:
let text = 'If there would be more <div>mathces <div>in</div> string</div>, you will merge them to one';
const p = document.createElement('p');
p.innerHTML = text;
p.querySelector('div').innerText = p.querySelector('div').innerText;
console.log(p.innerHTML);

HTML Button Appears as Text after $compile [duplicate]

This question already has answers here:
how can we use $compile outside a directive in Angularjs
(5 answers)
"Thinking in AngularJS" if I have a jQuery background? [closed]
(15 answers)
Closed 4 years ago.
I am trying to create a pop up dialog with two buttons created in JS code with angular. The following code that produces the buttons...
var html = $('<button ng-click = "cancelAlert()" > Cancel</button > <button ng-click="continueAlert()">Continue</button>');
var div = $compile(html);
var content = div($scope);
document.getElementById('dialogboxhead').innerHTML = "header";
document.getElementById('dialogboxbody').innerHTML = "body";
document.getElementById('dialogboxfoot').innerHTML = content;
Gives me the following html text instead of the actual buttons themselves...
[[object HTMLButtonElement], [object Text], [object HTMLButtonElement]]
Am I missing something here that I have forgotten to add in?
The HTML looks like the following...
<div id="dialogoverlay"></div>
<div id="dialogbox">
<div>
<div id="dialogboxhead"></div>
<div id="dialogboxbody"></div>
<div id="dialogboxfoot"></div>
</div>
</div>
The $compile method accepts a string argument if you want to provide markup in this way.
Avoid wrapping the input for $compile with anything (ie $(..)). Instead, just pass the html string directly to the $compile() method, and also attach the div via the DOM append() method, and you should find this will work as expected:
var html = '<button ng-click="cancelAlert()">Cancel</button><button ng-click="continueAlert()">Continue</button>';
var div = $compile(html);
...
document.getElementById('dialogboxfoot').append( div[0] );
For more infromation see the usage on the official docs.
Here's a link to a working jsFiddle
It is not wise to mix AngularJS and jQuery this way.
The major problem with this approach is that $compile adds watchers to the specified scope. Those watchers will remain after added elements are removed from the DOM. This will result in memory leaks. If this is a dialog box that is constantly being added and removed -- beware.
But if you must, don't use innerHTML to append compiled content:
̶d̶o̶c̶u̶m̶e̶n̶t̶.̶g̶e̶t̶E̶l̶e̶m̶e̶n̶t̶B̶y̶I̶d̶(̶'̶d̶i̶a̶l̶o̶g̶b̶o̶x̶f̶o̶o̶t̶'̶)̶.̶i̶n̶n̶e̶r̶H̶T̶M̶L̶ ̶=̶ ̶c̶o̶n̶t̶e̶n̶t̶;̶
var foot = document.getElementById('dialogboxfoot');
$(foot).append(content);
The DEMO
angular.module("app",[])
.controller("ctrl",function($scope, $compile) {
var html = $('<button ng-click = "cancelAlert()" > Cancel</button > <button ng-click="continueAlert()">Continue</button>');
var div = $compile(html);
var content = div($scope);
document.getElementById('dialogboxhead').innerHTML = "header";
document.getElementById('dialogboxbody').innerHTML = "body";
var foot = document.getElementById('dialogboxfoot');
$(foot).append(content);
})
<script src="//unpkg.com/jquery"></script>
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="app" ng-controller="ctrl">
<div id="dialogoverlay"></div>
<div id="dialogbox">
<div id="dialogboxhead"></div>
<div id="dialogboxbody"></div>
<div id="dialogboxfoot"></div>
</div>
</body>

How to display just one element with same id

This is my first question at stack overflow
i just wanted to know a simple solution for the following case
<div *ngFor="let d of w.event">
<div class="date" id="d.date" >
<p>
<span style="font-size:1.75em">{{d.date | date:'dd'}}</span>
<br>
<strong> {{d.date | date:'EEE'}}</strong>
</p>
</div>
the looped div can have the same id
I just want to display the first div with a particular date and ignore the rest
can this be achieved with CSS or JavaScript
You can't use the same id on two elements. It's one of the few restrictions on ids.
You can use a class:
<div class="show">Yes</div> <div class="show">No</div>
...and then show either the first or second by using index 0 or index 1 after getting a list of matching elements:
var list = document.querySelectorAll(".show");
list[0].style.display = "none"; // Hides the first one
// or
list[1].style.display = "none"; // Hides the second one
Some other thoughts:
1. Rather than using style.display as I did above, you might add a class that hides the element.
2. You might use separate ids (or classes) for the elements so you don't need to index, e.g.:
<div id="show-yes">Yes</div> <div id="show-no">No</div>
then
document.getElementById("show-yes").style.display = "none";
// or
document.getElementById("show-no").style.display = "none";
On all browsers in my experience, you can do the first thing above (with querySelectorAll) with your invalid HTML with a selector like "[id=show], but don't. Fix the HTML instead.
In your question update, you show:
<div *ngFor="let d of w.event">
<div class="date" id="d.date" >
...
You've said you're aware of the fact you can't have multiple elements with the same id, so why code that? You can easily give them unique ids:
<div *ngFor="let d of w.event; let i = index">
<div class="date" id="d.date{{i}}" >
...
First of all, in HTML ID is a unique selector so one ID can be associate with only one element. if you want to achieve your desired functionality you have to assign different id for both DIV. and use javascript to hide and show DIV
<div id="showYes">Yes</div> <div id="showNo">No</div>
If you want to show one at a time you can go with *ngIf , as it will show only one at a time
<div id="show" *ngIf='your_status'>Yes</div>
<div id="show" *ngIf='!your_status'>No</div>
After your question update , you can create custom filter that will only return unique date , so only first unique date will be shown
// CREATE A PIPE FILTER :
import { Pipe, PipeTransform } from '#angular/core';
#Pipe({name: 'checkUniqueDate'})
export class UniqueDatePipe implements PipeTransform {
transform(dataArray) {
let dates = [];
return dataArray.filter(data => {
return if(dates.indexOf(data.date) === -1) {
dates.push(data.date);
return true;
} else {
return false;
}
});
}
}
// TEMPLATE SIDE :
<div *ngFor="let d of (w.event | checkUniqueDate )">
Add the date in class also, then you can try below code
.YOUR_DATE{
display:none
}
.YOUR_DATE:first-child{
displany:inline
}

Trouble setting the HTML of a variable in JQuery

What I've done is loaded some HTML from a file and I am attempting to modify some elements within that HTML.
The initialization looks like this:
var id = player_info["ID"];
$("#main_container").append(
$("<div />").attr({class: "player_container", id: "player_" + id}).css("display", "none")
);
// Add all information to the player container
var player_container = $("#player_" + id);
player_container.load("player_layout.html");
With player_layout.html looking like this:
<div class="player_name">
</div>
<div class="player_chips">
Chips:
<br/>
<span class='bidding'></span>/<span class='chips'></span>
</div>
<div class="player_stats">
Wins / Losses
<br/>
<span class="wins"></span>/<span class="losses"></span>(<span class="total_games"></span>)
<br/><br/>
Chips Won / Chips Lost
<br/>
<span class="chips_won"></span>/<span class="chips_lost"></span>
</div>
<button class="player_won">Player Has Won</button>
I then want to modify some of the elements, specifically classes. An example of the way I was initially doing this is:
player_container.find(".player_name").text(player_info['username']);
This wasn't working so I then tried to switch find with children and text with html but that didn't seem to work. I then tried this:
$('> .player_name', player_container).html(player_info['username']);
but that also didn't work. I understand that I can use DOM to grab the childNodes and compare the class names but there are a lot of classes that need modifying and I'd also like to know if this is possible in JQuery. Thanks in advance for any help.
You need to use complete callback method of .load()
var player_container = $("#player_" + id);
player_container.load("player_layout.html", function(){
player_container.find(".player_name").text(player_info['username']);
});

How to get the unique selector of elements [duplicate]

This question already has answers here:
What is the path in the html dom for a selected element? [duplicate]
(2 answers)
Closed 9 years ago.
I am doing an editor where when user clicks a text or anything in the html document, I get it changed and record the changes in the database. It is a widget I am doing. Thus this is my question. Suppose I have an html element of the following structure:
<html>
<table>
<tr>
<td>Apple</td>
<td>Pear</td>
</tr>
<tr>
<td>Chicken</td>
<td>Beef</td>
</tr>
</table>
<div id="one">
<p>Red</p>
<p>Orange</p>
</div>
<div class="two">
<p>Green</p>
<p>Purple</p>
</div>
<div class="two">
<p>Black</p>
<p>Blue</p>
</div>
</html>
When user clicks Apple, the unique path is (Jquery):
table:eq(0)>tr:eq(0)>td:eq(1)
Suppose user clicks Red, the unique path is (Jquery):
div#one:eq(o) > p:eq(0)
Is there any plugin readily available to uniquely identify the selectors?
Currently I am using this function:
function getUniquePath2(node){
var parentEls = node.parentsUntil('[id]').add( node.closest('[id]') )
.map(function () {
return this.tagName + (this.id ? '#' + this.id : "");
})
.get().join(">");
return parentEls;
}
However the above won't support and I have been researching for 4 days and I don't get an idea of how I should do it. The reason I am doing this because I want to track the user changes and change the elements as per the draft the user has made. The issue is, some elements do not have id's and the xpath conflicts with other elements. For instance if I want to change Green. How would it be unique anymore?
What about something like this ?
var getUniquePath = function( node ) {
var parts = [ ];
$( node ).parents().each( function( index, element ) {
parts .push( element.tagName + '[' + $(element).index() + ']' );
});
return parts.join( '', parts.reverse() );
}

Categories