Match/group HTML elements by their data attributes values - javascript

In the HTML I have multiple elements that are grouped by data attributes, but these attributes don't necessarily have the same parent:
Example:
<span data-toggle-trigger="1">Resources</span>
<p>......<p>
<span data-toggle-trigger="2">Alpha</span>
<div data-toggle-container="1"></div>
<p>......<p>
<span data-toggle-trigger="3">Beta</span>
<div data-toggle-container="2"></div>
<div data-toggle-container="3"></div>
The used attributes are:
data-toggle-trigger` and `data-toggle-container
The behavior I need:
When someone click on data-toggle-trigger something happens on data-toggle-container (like show/hide).
To get them:
triggers = document.querySelectorAll(['data-toggle-trigger']);
container = document.querySelectorAll(['data-toggle-container']);
It looks like I have two options:
To get all of them and try to group them
To get only triggers and than search after that for containers
I'm inclining for 1), but I don't know what the best option is to group.
My naive approach is to think/use 2 for nested loops and loop and compare value, but I don't know if is the best option. So I'm looking for some opinion regarding a good approach.
Important - I'm using pure JavaScript, no ECMAScript 6 syntax sugar (because of other stuff).

This is opinion-based, but I think the most efficient solution is to have only one event listener, possibly on <body>, and if the target has .dataset.toggleTrigger, then show/hide all matching elements, i.e., querySelectorAll('[data-toggle-container="' + x + '"]') in a loop.

I would add classes to manage your triggers and containers. Here is example: Fiddle.
In this snippet I add text every time you click on a trigger.
function myFunction(numberOfTriggerClicked) {
var element = document.querySelectorAll("[data-toggle-container='"+numberOfTriggerClicked+"']")[0];
element.innerText += " NEW TEXT";
}

Related

Changing inner HTML of a button with dynamic id

On a project I'm working on, a HTML file is defining a Javascript template used on selection buttons. All buttons have a "Change..." label that I want to localize (set dynamically). In other cases I'm searching for the element ID and setting the InnerHTML accordingly. But in this case, the ID of the buttons are defined dynamically. Is it possible to have a text element inside the button element, search for this element, and set its InnerHTML value?
<script id="optionSelectionTemplate" type="text/x-handlebars-template">
<div class="sub-section option-selection">
{{#if name}}<h4>{{name}}</h4>{{/if}}
<div class="current"></div><button class="button" id="{{id}}" data-action-id="{{id}}">Change...</button>
</div>
</script>
I've been searching this for a while now. But given that my forte is not web development, I'm not really sure what to search for...
You may be able to get the button element(s) by its class instead; for example:
var x = document.getElementsByClassName("button");
As you suggested, you can improve your selection's precision by first getting the 'optionSelectionTemplate' element(s) like so:
var x = document.getElementById("optionSelectionTemplate").getElementsByClassName("button");
Or if you prefer:
var x = document.getElementById("optionSelectionTemplate").getElementsByTagName("button");
Here are some links for more on these method:
https://www.w3schools.com/jsref/met_document_getelementsbyclassname.asp
https://www.w3schools.com/jsref/met_document_getelementsbytagname.asp
Depending on how dynamic your localization should become, you could also specify the text inside a (locale-dependent) CSS as in https://jsfiddle.net/1gws5kat/ :
[HTML]
<button class="button btn_change" id="{{id}}" data-action-id="{{id}}"></button>
[CSS]
.btn_change:before { content: "Change..."; }
In particular when dealing with a large number of identically-named elements (i.e. many "Change" buttons), this might be pretty handy.
You find those btns by this command:
var btnlist= $(':button')
This Camano get you all button in your html file, then loop ton in and apply your changing.
Before call this command, jquery must be install.

Which method is better/ faster for performance - createElement or innerHTML? [duplicate]

This question already has answers here:
Advantages of createElement over innerHTML?
(5 answers)
Closed 6 years ago.
I'm making divs with several sub elements something like this:
<div class="item" data-id="28">
<div class="action-btns">
<button class="add"></button>
<button class="rmv"></button>
</div>
<div class="info">
<h3>Title here</h3>
<span>caption here</span>
</div>
</div>
And im giving functions to those two buttons on click. I'm wondering which method of creating these items is better - createElement or innerHTML?
As far as createElement goes i like it because i can bind the onclick while creating the button inside the element and then append it. Also im wondering if appending this "item" to the parent div is faster / better than updating innerHTML += "something";
As far as innerHTML goes there are fewer lines of code to write, but also i have to either write the onclick="myFunction()" inside the buttons instead of adding it dynamically.
Please no jQuery or anyting but pure Js. Thanks for your time :)
https://jsperf.com/innerhtml-vs-createelement-test (Note i did not write this)
Results in chrome is about 60% ish percent slower using createElement. As per (#Sagar V and #Hajji Tarik) answer consider more then just speed.
There are several advantages to using createElement instead of modifying innerHTML (as opposed to just throwing away what's already there and replacing it) besides safety, like Pekka already mentioned:
Preserves existing references to DOM elements when appending elements
When you append to (or otherwise modify) innerHTML, all the DOM nodes inside that element have to be re-parsed and recreated. If you saved any references to nodes, they will be essentially useless, because they aren't the ones that show up anymore.
Preserves event handlers attached to any DOM elements
This is really just a special case (although common) of the last one. Setting innerHTML will not automatically reattach event handlers to the new elements it creates, so you would have to keep track of them yourself and add them manually. Event delegation can eliminate this problem in some cases.
Could be simpler/faster in some cases
If you are doing lots of additions, you definitely don't want to keep resetting innerHTML because, although faster for simple changes, repeatedly re-parsing and creating elements would be slower. The way to get around that is to build up the HTML in a string and set innerHTML once when you are done. Depending on the situation, the string manipulation could be slower than just creating elements and appending them.
Additionally, the string manipulation code may be more complicated (especially if you want it to be safe).
Here's a function I use sometimes that make it more convenient to use createElement.
function isArray(a) {
return Object.prototype.toString.call(a) === "[object Array]";
}
function make(desc) {
if (!isArray(desc)) {
return make.call(this, Array.prototype.slice.call(arguments));
}
var name = desc[0];
var attributes = desc[1];
var el = document.createElement(name);
var start = 1;
if (typeof attributes === "object" && attributes !== null && !isArray(attributes)) {
for (var attr in attributes) {
el[attr] = attributes[attr];
}
start = 2;
}
for (var i = start; i < desc.length; i++) {
if (isArray(desc[i])) {
el.appendChild(make(desc[i]));
}
else {
el.appendChild(document.createTextNode(desc[i]));
}
}
return el;
}
If you call it like this:
make(["p", "Here is a ", ["a", { href:"http://www.google.com/" }, "link"], "."]);
you get the equivalent of this HTML:
<p>Here is a link.</p>
The answer of #Matthew Crumley
innerHTML just put the plain text. Whereas createElement creates the element object and adds to Parent.
since browser convert tags in plain text to HTML element, it is converted to tags. It is not recommended.
createElement is the recommended method

Get HTML Node (Not Necessarily Element) On Click

I have a web application that, after clicking any node in the HTML, needs to retrieve the index of that node in its parent's childNodes array. However, I am having trouble getting the currently selected node through an onclick event. The returned target of the event is the containing element rather than the specific node inside the element. This difference is important when text nodes exist, such as:
<div>This is Node 1<span>node 2</span>, node 3, and <span>node 4</span></div>
If you click on the spans for Node 2 or Node 4, it's straightforward to know where you are. However, if you click on the text for Node 1 and Node 3, I can't seem to find where the event would help you figure out which part of the actual content was clicked on.
This happens to be important because a later operation needs to check for certain properties either forward or backward through the document until the first match. So, if both Node 2 and Node 4 are a match for the search, I need to know if I am in Node 1 or Node 3 in order to know which one to return. For example, if searching rightwards, starting in Node 1 means that Node 2 should be returned, and starting in Node 3 means that Node 4 should be returned. Obviously, this is a simplification, but it demonstrates the issue. Does anyone know the canonical solution for this? If I can get the node object or the index, that should be sufficient. jquery is fine, but not necessary.
Maybe somthing like this demo could help you out a bit:
document.getElementsByTagName('div')[0].addEventListener('click', function () {
var fullStr = this.innerHTML.replace(/<[^>]*>/g, ''),
sel = window.getSelection(),
str = sel.anchorNode.data,
clickPos = sel.focusOffset,
wordPosLeft = str.slice(0, clickPos + 1).search(/\S+$/),
wordPosRight = str.slice(clickPos).search(/\s/),
wordClicked,
nextWordRegex,
nextWordPosLeft,
nextWord;
if(wordPosRight < 0) {
wordClicked = str.slice(wordPosLeft);
} else {
wordClicked = str.slice(wordPosLeft, wordPosRight + clickPos);
}
nextWordRegex = new RegExp(wordClicked);
nextWordPosLeft = fullStr.search(nextWordRegex) + wordClicked.length;
nextWord = fullStr.slice(nextWordPosLeft).match(/^\s*(\S*)\s*.*$/)[1];
console.log('wordClicked: ' + wordClicked);
console.log('nextWord: ' + nextWord);
});
See this fiddle.
You need to get Your nodes in some containers. If You would click on "Node 1" text, function will return You a <div> element. But, if You would change Your code on this:
<div>
<span>This is Node 1</span>
<span>node 2</span>
<span>, node 3, and </span>
<span>node 4</span>
</div>
it would work and return <span> container. Not possible in other way, I think.
You can eventually make some JavaScript split() or regex operations.
If you're just trying to work out the text of the element you clicked, minus child nodes text, I have a solution:
$('body').on('click', function(e) {
alert('Node Text: '+$(e.target).clone().children().remove().end().text());
});
http://jsfiddle.net/xoegujqu/1/
Essentially, delegate the click event to the highest-level element you want this to run for (in this example it just used body, but you'll probably want to be more specific). use $(e.target) to get the element that was actually clicked, .clone() to clone it so you can modify it without affecting the actual page content, .children().remove() to remove all it's descendant elements, .end() to go back to the previous jQuery selector object, then finally .text() to get the remaining text content.
check out even bubbling / propagation
Also: https://developer.mozilla.org/en-US/docs/Web/API/EventTarget.addEventListener
useCapture section
It is not possible to do this as far as I know. You cannot:
Detect events on text nodes.
Detect the position of the text node relative to window or page.
This answer gives an idea with some good insight, but does not do what you want (return the index of the node).
I believe you are out of luck, unless you can find a way to use the solution above to determine index.

How do I make an OnClick function that will change a word when a user clicks it to another word?

Okay so, I want to make an OnClick function in JavaScript that makes it so when a user clicks on it, it will change the word. Is there a replaceword() function or something that which will let me do so? I know this is not real code, but for example:
<p>Quickly <span onclick="replaceword('Surf');">Search</span> The Web!</p>
If there is, then can someone tell me also how to reverse the code maybe? So when they click on it the second time, it will change back to "Search"?
If you want to jump between multiple words, you'll need to store them someplace. You could have two words in the sentence, and toggle the visibility of one or the other (which doesn't scale well), or you could even store them as values on an attribute placed on the element itself.
<p>Hello, <span data-values="World,People,Stack Overflow">World</span>.</p>
I have placed all possible values within the data-values attribute. Each distinct value is separated from the other values by a comma. We'll use this for creating an array of values next:
// Leverage event-delegation via bubbling
document.addEventListener( "click", function toggleWords ( event ) {
// A few variables to help us track important values/references
var target = event.target, values = [], placed;
// If the clicked element has multiple values
if ( target.hasAttribute( "data-values" ) ) {
// Split those values out into an array
values = target.getAttribute( "data-values" ).split( "," );
// Find the location of its current value in the array
// IE9+ (Older versions supported by polyfill: http://goo.gl/uZslmo)
placed = values.indexOf( target.textContent );
// Set its text to be the next value in the array
target.textContent = values[ ++placed % values.length ];
}
});
The results:
The above listens for clicks on the document. There are numerous reasons why this is a good option:
You don't need to wait for the document to finish loading to run this code
This code will work for any elements added asynchronously later in the page life
Rather than setting up one handler for each element, we have one handler for all.
There are some caveats; you may run into a case where the click is prevented from propagating up past a particular parent element. In that case, you would want to add the eventListener closer to your target region, so the likeliness that bubbling will be prevented is less.
There are other benefits to this code as well:
Logic is separated from markup
Scale to any number of values without adjusting your JavaScript
A demo is available for your review online: http://jsfiddle.net/7N5K5/2/
No, there isn't any native function, but you can create on your own.
function replaceword(that, word, oword) {
that.textContent = that.textContent == oword ? word : oword;
}
You can call it like this:
<p>Quickly<span onclick="replaceword(this,'Surf','Search');">Search</span>The Web!</p>
Live Demo: http://jsfiddle.net/t6bvA/6
<p id="abc">Hello</p>
<input type="submit" name="Change" onclick="change()">
function change(){
var ab=document.getElementById('abc').value;
ab.innerHTML="Hi, Bye";
}
I think so this should help you, you should go to site such as w3schools.com, its basic and it will answer your doubt
You can try something like this if you wanna use jQuery
http://jsfiddle.net/R3Ume/2/
<script src="//code.jquery.com/jquery-1.10.2.js"></script>
<body>
<p>Hello <a id='name'>John<a></p>
<input id="clickMe" type="button" value="replace" onclick="onClick();" />
<script>
function onClick() {
$('#name').text('world');
}
</script>

javascript - remove element/node with no ID and specific content

I'm struggling to decipher a way to remove several specific href elements which contain no IDs and are children of individual parents with no IDs.
The best I can manage is identifying the four offending, out of 8 or 9 href tags (and the number may vary), by a specific word within the URL itself. For this, I do the following:
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
if(ptn.exec(xx[i])){
alert(xx[i]);
}
}
</script>
Of course all this gives me is the four specific URLs within the href where "=media" is present. Now, somehow, I need to be able to remove either these href elements, or their parent elements (which happen to be unordered list tags). It's not until I get a level higher (table cell) that I gain access to an element ID, or anything distinguishing besides a particular word within the URL itself.
I'm open to any approach at this point - PHP may be an option (I really haven't explored this yet), but for this, javascript was my first logical choice. I can't tamper with the page that generates the links directly, only a secondary page which gets included at page load time.
Any pointers on how to solve this??
======================== final solution =====================
<script language=javascript>
var xx = document.getElementById('theID').getElementsByTagName('a');
var ptn=/\=media/;
for(var i=0; i<xx.length; i++) {
while(ptn.exec(xx[i].href)){
alert(xx[i]);
xx[i].parentNode.removeChild(xx[i]);
}
}
</script>
You don't need the ID to remove an element. You only need a reference to the element (which you seem to have).
instead of this:
alert(xx[i]);
try this:
XX[i].parentElement.removeChild(xx[i]);
You can call removeChild() on the parent element, like so:
xx[i].parentNode.removeChild(xx[i]);
As a side note, your regular expression isn't being executed on the href property. Change your if statement to:
if(ptn.exec(xx[i].href)){
var parent = xx[i].parentNode;
parent.removeChild(xx[i]);
http://www.onlinetools.org/articles/unobtrusivejavascript/chapter2.html has some nice examples of similar operations (scroll down).

Categories