looping through <article> in the html page - javascript

I have to loop through <article> tag in the Html file and add its classname when clicked and then remove the class name in all other <article> tags
for eg:
<article onclick="javascript:selectthis(this)">
1
</article>
<article onclick="javascript:selectthis(this)">
11
</article>
<article onclick="javascript:selectthis(this)">
111
</article>
<article onclick="javascript:selectthis(this)">
1111
</article>
<article onclick="javascript:selectthis(this)">
11111
</article>
<script>
function selectthis(THIS) {
THIS.className = "countrySelected";
}
</script>
is there a way to loop through the tags and remove the classnames except for the recent one that is clicked? What is the best possible way to do ? Should I really have to add a onclick event attached to every <article> tag? because there could be many <article> tag in the html file. I really donot want to add this onclick event to every article tag.
any help would be appreciated.

You didn't tag jQuery , but I'd recommend using that in this particular case, where event bubling and filtering plays nice. Instead of register multiple click-handlers (as you point out) you can register one soley, and have that one in common for all elements. jQuery is good at filtering out events to a specific selector as well. I'd give it a shot.
Here's an example of how to solve your issue using jQuery:
// Register an event-handler on document, that listens for a 'click' event.
// Only pick up the event from the selector 'article' though, which corresponds
// to only article elements on the page.
$(document).on('click', 'article', function(){
this.className = "countrySelected"; // this in this context, is the actual DOM-element
});
Then for your issue of removing the className for all articles, not having the class countrySelected. This also is nice with jQuery as such:
// Find all article-elements, that DON'T have the class countrySelected
// Then remove _all_ the classes it has
// To remove a specific class, use .removeClass('classToRemove')
$('article').not('.countrySelected').removeClass();
So in the end, you can combine the two and make this:
$(document).on('click', 'article', function(){
$('article').removeClass(); // Remove class from all other first
this.className = "countrySelected"; // this in this context, is the actual DOM-element
});

Assuming you do place an onclick on each article .. inside of your function, you could do this:
function selectthis(THIS) {
//remove the css classes from all articles
var articles=document.getElementsByTagName('article');
for(var a=0;a<articles.length;a++){
articles[a].setAttribute('class','');
}
//add class back on for this article
THIS.className = "countrySelected";
}

Add this jquery:
$("article").click(function() {
$("article").toggleClass("countrySelected");
});
Example code snippet:
$("article").click(function() {
$(".countrySelected").removeClass("countrySelected");
$(this).addClass("countrySelected");
});
article.countrySelected {
background-color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<article>This is article 1</article>
<article>This is article 2</article>
<article>This is article 3</article>
<article>This is article 4</article>
<article>This is article 5</article>
<article>This is article 6</article>
Since you didn't tag this jquery, here is a pure javascript solution:
window.onload = function() {
// This is a list of all the articles on the page
var articles = document.querySelectorAll("article");
for (var i = 0; i < articles.length; ++i) {
// Loop through each article
var article = articles[i];
// Add an event listener for each one
article.addEventListener("click", function() {
// Get the previously selected one and deselect it
var prevSelected = document.querySelector(".countrySelected");
if (prevSelected != null) prevSelected.classList.remove("countrySelected");
this.classList.add("countrySelected"); // or for older browsers: this.className = "countrySelected";
}, false);
}
}
window.onload = function() {
var articles = document.querySelectorAll("article");
for (var i = 0; i < articles.length; ++i) {
var article = articles[i];
article.addEventListener("click", function() {
var prevSelected = document.querySelector(".countrySelected");
if (prevSelected != null) prevSelected.classList.remove("countrySelected");
this.classList.add("countrySelected"); // or for older browsers: this.className = "countrySelected";
}, false);
}
}
.countrySelected {
background-color: orange;
}
<article>Article 1</article>
<article>Article 2</article>
<article>Article 3</article>
<article>Article 4</article>
<article>Article 5</article>

I'll treat it this way without using jQuery. The reason why is this way in the future it is far more easier to understand why certain stuff happens. And sometimes basic JS is far more fitting for using a small adjustment instead of a bloated library.
The best way is to start, is at the moment nothing still happened and the page just loaded. Instead of using the javascript inline with an onclick. First assign an event-listener.
Page Loaded
With Element.getElementsByTagName() you can get all the needed elements and go for a loop. documentation;
So you get
var articleTags = document.getElementsByTagName('ARTICLE');
In this case we can use a simple loop
var articleDOM = null;
for(var i = 0; i < articlesTags.length; i++){
articleDOM = articlesTags[i];
articleDOM.addEventListener("click", addClassToArticleFunction);
)
Each article now has an event binded with a function. Let's define that function.
Add Class Function
I'll use 'addClassToArticleFunction', but you can of course make it even more generic in name. Could be you want also use this function for other tags or DOM-elements.
function addClassToArticleFunction(){
this.className = "countrySelected";
}
Each time this will be called the class will be defined. Only you off course get a problem by all of them will get that classname.
So lets just call a function that will do all the deleting.
function addClassToArticleFunction(){
deleteClassNameFromArticles();
this.className = "countrySelected";
}
function deleteClassNameFromArticles(){
//deletion here
}
Since we know all the articles already, and know how to calls the classname it is an easy copy paste
for(i = 0; i < articleTags.length; i++){
articleDOM = articleTags[i];
articleDOM.className = "";
}
At this point it is done.
Recap
load window
assign event listeners to articles
when clicked on article a function will be called
this function will delete all classnames from articles
this function will assign on the clicked article a classname
JS fiddle to see it work

Related

Adding javascript programmatically to dynamicly generated hyperlinks with the same id

Might be a strange setup, but I have a number of hyperlinks on the page with the same id (yeah, I know, but it was not my choice and I cannot change that at this time plus those hyperlinks are generated dynamically).
Example:
<div id="Links">
<div class="myItem">Some text</div>
<div class="myItem">More text</div>
<div class="myItem">Even more text</div>
</div>
Now I need to attach javascript to those links dynamically (the hyperlinks are also dynamically generated). The easiest way I see is by getting all hyperlinks on the page and then check the hyperlink id to ensure I only take care of those that have id of "myLink" (I have many other hyperlinks on the page).
I thought of using getElementById but that would only grab the first element with the specified id.
am attaching javascript to those links using the following:
window.onload = function() {
var anchors = document.getElementsByTagName('a');
for(var i = 0; i < anchors.length; i++) {
var anchor = anchors[i];
if (anchor.id='myLink')
{
if (anchor.getAttribute("LinkID") != null)
{
anchor.onclick = function() {
MyFunction(this.getAttribute("LinkID"), false);
return false;
}
}
}
}
}
The above function works fine, but it creates another issue - affects the styling of other hyperlinks on the page. So I was wondering if there is a way to accomplish the same thing but without affecting other elements on the page?
This is more modern and corrects your equality test:
window.onload = function() {
var anchors = document.getElementsByTagName('a');
for(var i = 0; i < anchors.length; i++) {
if (anchor[i].id==='myLink' && anchor[i].getAttribute("LinkID") !== null)
{
anchor[i].addEventListener("click", function() {
MyFunction(this.getAttribute("LinkID"), false);
}
}
}
}
Even with your original code, I don't see anything that would interfere with styling in the code. Can you elaborate as what styling changes you were getting?
You can use an attribute selector and document.querySelector([id=<id>]) pretty reliably depending on your browser support situation: http://codepen.io/anon/pen/YwLdKj
Then, of course, loop through that result and make subsequent changes or event bindings.
If not, you could use jQuery (referenced in above code pen).
You might also use JavaScript event delegation and listen for all click events, check if the user is clicking a link with the correct id.
If a combination of tag == 'a', class == "myItem" and presence of a LinkID attribute is sufficient to identify nodes requiring a click handler they could be identified using multiple CSS selectors. If this is not possible however, a query selector not using id can create a list of nodes to be checked for id, as for example:
function callMyFunction()
{ MyFunction(this.getAttribute("LinkID"), false);
}
function addClickHandlers()
{ var list = document.querySelectorAll("a[LinkID]")
var i, node;
for( i = 0; i < list.length; ++i)
{ node = list[i];
if(node.id == "myLink")
{ node.onclick=callMyFunction;
}
}
}
See also running a selector query on descendant elements of given node if of interest.

How can access bunch of <li>?

I have a list of texts and I want to change their innerHTML. how can I do that by javascript if I have thousands of li tag (whose data come from database)?
<div>
<ul>
<li>a</li>
<li>as</li>
<li>asd</li>
<li>asds</li>
<li>asdsa</li>
<li>asdsad</li>
<li>asdsadz</li>
<li>asdsadzc</li>
....
.....
</ul>
</div>
-Thanks.
Update
JS code being used:
function a(){
var parent = document.getElementById("z");
var i = 0;
for(i = 0; i <= parent.children.length; i++){
if(parent.children[i].tagName == "LI"){
if(i%2!=0){
parent.children[i].innerHTML="ok";
}
}
}
}
document.onload=a(); // this didn't work. so I called the function in body tag instead of that.
<body onload="a();">
Have you tried using getElementsByTagName ? Sonds like it would help you find the elements you're trying to work with.
Edit
If you can give an Id to the UL element that holds the li's you're trying to process, you could do something like this:
var parent = document.getElementById("yourID");
var i = 0;
for(i = 0; i < parent.children.length; i++){
if(parent.children[i].tagName == "LI") {
//do what you want...
}
}
EDit 2
You have to change the last line on your script:
document.onload=a();
With this one: window.onload=a;
That'll get your function to execute on the onLoad event. Note that there might be some crossbrowser incompatibility, I would suggest researching a bit on how to execute functions on the onload event on a crossbrowser manner, or just adding this to your body tag:
<body onload="a();">
Given the - not so far fetched - precondition you wish to use jQuery, you can select them and iterate over them with "each".
$("li").each(
function() { $(this).html("changed content"); }
);
If you are not using jQuery, using a js-library that helps you out with the quircky dom is probably not a bad idea...
The general idea
Select nodes
Iterate and change html
is always the same.

Javascript click event listener on multiple elements and get target ID

I have a javascript file that sets an EventListener of 'click' on every element with the <article> tag. I want to get the id of the article clicked when the event fires. For some reason, my code produces nothing!
My javascript:
articles = document.getElementsByTagName('article');
articles.addEventListener('click',redirect(e),false);
function redirect(e){
alert(e.target.id);
}
Why isn't this working? BTW my article setup is in a function called when the window is loaded, and i know that works for sure because that function has other stuff that work.
EDIT
So i fixed my code so it will loop and add the listener to every article element, and now i get an alert box with nothing in it. When trying to output the e.target without the ID, i get the following message for every element:
[object HTMLHeadingElement]
Any suggestions?
ANOTHER EDIT
My current javascript code:
function doFirst(){
articles = document.getElementsByTagName('article');
for (var i = 0; i < articles.length; i++) {
articles[i].addEventListener('click',redirect(articles[i]),false);
}
}
function redirect(e){
alert(e.id);
}
window.addEventListener('load',doFirst,false);
This is showing my alert boxes when the page finished loading, without considering that i haven't clicked a damn thing :O
You are not passing an article object to redirect as a parameter.
Try this (EDIT):
articles = document.getElementsByTagName('article');
for (var i = 0; i < articles.length; i++) {
articles[i].addEventListener('click',redirect,false);
}
function redirect(ev){
alert(ev.target.id);
}
Hope, it will solve the bug.
Why is nobody mentioning a single event which checks for the clicked element?
document.body.addEventListener('click', function(e) {
var target = e.target;
if (target.nodeName === 'article') {
// do whatever you like ;-)
}
e.stopPropagation()
});
it's more performant to have less events..
if you don't need to check for click events on the whole body you could attach the event to some closer parent element
You are executing the redirect function instead of passing the reference, try this:
articles = document.getElementsByTagName('article');
articles.addEventListener('click',redirect,false);
function redirect(e){
alert(e.target.id);
}
Edit:
Also, getElementsByTagName returns an array with articles, so you have to loop through them and call addEventListener on each one of them.
articles = document.getElementsByTagName('article');
for (var i = 0; i < articles.length; i++) {
articles[i].addEventListener('click',redirect,false);
}
function redirect(e){
alert(e.target.id);
}
getElementsByTagName returns a nodelist. You can then add an eventlistener to each one of those elements with a for loop.
<div id="test">
hey
</div>
<div id="test2">
yo
</div>
<script>
var nl = document.getElementsByTagName('div');
for(var i=0; i<nl.length; i++){
nl[i].addEventListener('click', function(e){
alert(e.target.id);
},false);
}
</script>
This mini javascript libary (1.3 KB) can do all these things
https://github.com/Norair1997/norjs/
nor.event(["#first"], ["touchstart", "click"], [doSomething, doSomething]);
This plugin can handle such stuff and more
Sry for bumping this old post, but I would do something like this
const myElements = document.querySelectorAll("article")
myElements.forEach(x => x.setAttribute("onclick", "alertFunction(this.id)"))
function alertFunction(theId){
alert(theId)
}
That would be if JavaScript is what you need. But today you can use JQuery instead, and that would be less code.
articles.addEventListener('click',redirect,false); // omit redirect(e) inside event listener // and then try with the alert as you did.
if does not work then try by e.id instead of e.target.id inside alert as below:
alert(this.id);
Thanks.

AJAX: Applying effect to CSS class

I have a snippet of code that applies a highlighting effect to list items in a menu (due to the fact that the menu items are just POST), to give users feedback. I have created a second step to the menu and would like to apply it to any element with a class of .highlight. Can't get it to work though, here's my current code:
[deleted old code]
The obvious work-around is to create a new id (say, '#highlighter2) and just copy and paste the code. But I'm curious if there's a more efficient way to apply the effect to a class instead of ID?
UPDATE (here is my updated code):
The script above DOES work on the first ul. The second ul, which appears via jquery (perhaps that's the issue, it's initially set to hidden). Here's relevant HTML (sort of a lot to understand, but note the hidden second div. I think this might be the culprit. Like I said, first list works flawlessly, highlights and all. But the second list does nothing.)?
//Do something when the DOM is ready:
<script type="text/javascript">
$(document).ready(function() {
$('#foo li, #foo2 li').click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
//When a link in div is clicked, do something:
$('#selectCompany a').click(function() {
//Fade in second box:
//Get id from clicked link:
var id = $(this).attr('id');
$.ajax({
type: 'POST',
url: 'getFileInfo.php',
data: {'id': id},
success: function(msg){
//everything echoed in your PHP-File will be in the 'msg' variable:
$('#selectCompanyUser').html(msg)
$('#selectCompanyUser').fadeIn(400);
}
});
});
});
</script>
<div id="selectCompany" class="panelNormal">
<ul id="foo">
<?
// see if any rows were returned
if (mysql_num_rows($membersresult) > 0) {
// yes
// print them one after another
while($row = mysql_fetch_object($membersresult)) {
echo "<li>"."".$row->company.""."</li>";
}
}
else {
// no
// print status message
echo "No rows found!";
}
// free result set memory
mysql_free_result($membersresult);
// close connection
mysql_close($link);
?>
</ul>
</div>
<!-- Second Box: initially hidden with CSS "display: none;" -->
<div id="selectCompanyUser" class="panelNormal" style="display: none;">
<div class="splitter"></div>
</div>
You could just create #highlighter2 and make your code block into a function that takes the ID value and then just call it twice:
function hookupHighlight(id) {
var context = document.getElementById(id);
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
}
hookupHighlight("highliter1");
hookupHighlight("highliter2");
jQuery would make this easier in a lot of ways as that entire block would collapse to this:
$("#highlighter1 li, #highlighter2 li").click(function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
If any of the objects you want to click on are not initially present when you run this jQuery code, then you would have to use this instead:
$("#highlighter1 li, #highlighter2 li").live("click", function() {
// do ajax stuff
$(this).siblings('li').removeClass('highlight');
$(this).addClass('highlight');
});
change the replace in /highlight/ig, it works on http://jsfiddle.net/8RArn/
var context = document.getElementById('highlighter');
var items = context.getElementsByTagName('li');
for (var i = 0; i < items.length; i++) {
items[i].addEventListener('click', function() {
// do AJAX stuff
// remove the "highlight" class from all list items
for (var j = 0; j < items.length; j++) {
var classname = items[j].className;
items[j].className = classname.replace(/highlight/ig, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
}, false);
}
So all those guys that are saying just use jQuery are handing out bad advice. It might be a quick fix for now, but its no replacement for actually learning Javascript.
There is a very powerful feature in Javascript called closures that will solve this problem for you in a jiffy:
var addTheListeners = function (its) {
var itemPtr;
var listener = function () {
// do AJAX stuff
// just need to visit one item now
if (itemPtr) {
var classname = itemPtr.className;
itemPtr.className = classname.replace(/\bhighlight\b/i, '');
}
// set the "highlight" class on the clicked item
this.className += ' highlight';
itemPtr = this;
}
for (var i = 0; i < its.length; i++) {
its[i].addEventListener ('click', listener, false);
}
}
and then:
var context = document.getElementById ('highlighter');
var items = context.getElementsByTagName ('li');
addTheListeners (items);
And you can call add the listeners for distinct sets of doc elements as many times as you want.
addTheListeners works by defining one var to store the list's currently selected item each time it is called and then all of the listener functions defined below it have shared access to this variable even after addTheListeners has returned (this is the closure part).
This code is also much more efficient than yours for two reasons:
You no longer iterate through all the items just to remove a class from one of them
You aren't defining functions inside of a for loop (you should never do this, not only for efficiency reasons but one day you are going to be tempted to use that i variable and its going to cause you some problems because of the closures thing I mentioned above)

How are they doing the accordion style dropdown on the following website?

I was taking a look at http://www.zenfolio.com/zf/features.aspx and I can't figure out how they are doing the accordion style expand and collapse when you click on the orange links. I have been using the Web Developer Toolbar add-on for firefox, but I have not been able to find anything in the source of the page like JavaScript that would be doing the following. If anyone knows how they are doing it, that would be very helpful.
This is actually unrelated, but if all you answers are good, who do I give the answer too?
They're setting the .display CSS property on an internal DIV from 'none' to '', which renders it.
It's a bit tricky, as the JS seems to be in here that's doing it:
http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js
But that's basically how everyone does this. It's in there, somewhere.
EDIT: Here it is, it looks like:
//... (bunch of junk)
zf_Features.prototype._entry_onclick = function(e, index)
{
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
while (cellNode.tagName != "TD") cellNode = cellNode.parentNode;
if (this._current != null) this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
this._current = "i" + index;
}
else this._current = null;
zf_frame.recalcLayout();
return false;
};
Basically, what they're doing is a really roundabout and confusing way of making the div inside of TD's with a class "desc" change to the class "desc open", which reveals its contents. So it's a really obtuse roundabout way to do the same thing everyone does (that is, handling onclick to change the display property of a hidden div to non-hidden).
EDIT 2:
lol, while I was trying to format it, others found it too. =) You're faster than I ;)
Using jQuery, this effect can be built very easily:
$('.titleToggle').click(function() {
$(this).next().toggle();
});
If you execute this code on loading the page then it will work with any markup that looks like the following:
<span class="titleToggle">Show me</span>
<div style="display:none">This is hidden</div>
Notice that this code will work for any number of elements, so even for a whole table/list full of those items, the JavaScript code does not have to be repeated or adapted in any way. The tag names (here span and div) don't matter either. Use what best suits you.
It is being done with JavaScript.
When you click a link, the parent td's class changes from 'desc' to 'desc open'. Basically, the expanded text is always there but hidden (display: none;). When it gets the css class of 'open' the text is no longer being hidden (display: block;).
If you look in the sitehome.js and sitehome.css file you can get an idea about how they are doing that.
btw I used FireBug to get that info. Even though there is some feature duplication with Web Developer Toolbar it's worth the install.
They're using javascript. You can do it too:
<div id="feature">
Feature Name
<div id="desc" style=="display:none;">
description here...
</div>
</div>
<script type="text/javascript">
function toggle()
{
var el=document.getElementById('desc');
if (el.style.display=='none')
el.style.display='block'; //show if currently hidden
else
el.style.display='none'; //Hide if currently displayed
}
</script>
The function above can be written using Jquery for smooth fade in/fade out animations when showing/expanding the descriptions. It has also got a SlideUp and Slidedown effect.
There is a lot of obfuscated/minified JS in their master JS include. It looks like they are scraping the DOM looking for H3's and checking for table cells with class desc and then processing the A tags. ( or some other order, possibly ) and then adding the onclick handlers dynamically.
if (this._current != null) this.dom(this._current).className
= "desc"; if ("i" + index != this._current) { cellNode.className = "desc open"; cellNode.id = this.id
+ "-i" + index; this._current = "i" + index; }
http://www.zenfolio.com/zf/script/en-US/safari3/windows/5AN2EHZJSZSGS/sitehome.js
The script is here.
The relevant section seems to be (re-layed out):
// This script seems over-complicated... I wouldn't recommend it!
zf_Features.prototype._init = function()
{
// Get a list of the H3 elements
var nodeList = this.dom().getElementsByTagName("H3");
// For each one...
for (var i = 0; i < nodeList.length; i++)
{
// ... set the onclick to be the function below
var onclick = this.eventHandler(this._entry_onclick, i);
// Get the first <a> within the H3 and do the same
var node = nodeList[i].getElementsByTagName("A")[0];
node.href = "#";
node.onclick = onclick;
// And again for the first <span>
node = nodeList[i].getElementsByTagName("SPAN")[0];
node.onclick = onclick;
}
};
zf_Features.prototype._entry_onclick = function(e, index)
{
// Get the parent node of the cell that was clicked on
var cellNode = this.dom().getElementsByTagName("H3")[index].parentNode;
// Keep going up the DOM tree until we find a <td>
while (cellNode.tagName != "TD")
cellNode = cellNode.parentNode;
// Collapse the currently open section if there is one
if (this._current != null)
this.dom(this._current).className = "desc";
if ("i" + index != this._current)
{
// Open the section we clicked on by changing its class
cellNode.className = "desc open";
cellNode.id = this.id + "-i" + index;
// Record this as the current one so we can close it later
this._current = "i" + index;
}
else
this._current = null;
// ???
zf_frame.recalcLayout();
return false;
};
Edit: added some comments
Unfortunately their code is in-lined and hard to read (http://www.zenfolio.com/zf/script/en-US/mozilla5/windows/5AN2EHZJSZSGS/sitehome.js), but this looks quite simple to implement... something along these lines (using prototypejs):
<script>
var showHide = {
cachedExpandable: null
,init: function() {
var containers = $$(".container");
for(var i=0, clickable; i<containers.length; i++) {
clickable = containers[i].getElementsByClassName("clickable")[0];
Event.observe(clickable, "click", function(e) {
Event.stop(e);
showHide.doIt(containers[i]);
});
}
}
,doIt: function(container) {
if(this.cachedExpandable) this.cachedExpandable.hide();
var expandable = container.getElementsByClassName("expandable")[0];
if(expandable.style.display == "none") {
expandable.show();
} else {
expandable.hide();
}
this.cachedExpandable = expandable;
}
};
window.onload = function() {
showHide.init();
};
</script>
<div class="container">
<div class="clickable">
Storage Space
</div>
<div class="expandable" style="display: none;">
Description for storage space
</div>
</div>
<div class="container">
<div class="clickable">
Galleries
</div>
<div class="expandable" style="display: none;">
Description for galleries
</div>
</div>
Its also caching the earlier expandable element, so it hides it when you click on a new one.

Categories

Resources