Newbie to Javascript here
I have a html/php code, Whats happening right now is when I click the button all elements are shown not just the targeted element. any help most appreciated
HTML/PHP
<div class="te contentDiv">
<div class="myContent" style="display:none">
<?=$text?>
<a id="close_btn"
href="javascript:toggle_visibility('myContent','close_btn','open_btn');"
class="close-more"><?=localised_string('Close')?></a>
</div>
</div>
JavaScript
var toggle_visibility = (function () {
function toggle(cl) {
var els = document.getElementsByClassName(cl);
for (var i = 0; i < els.length; i++) {
var s = els[i].style;
s.display = s.display === 'none' ? 'block' : 'none';
};
}
return function (cl) {
if (cl instanceof Array) {
for (var i = 0; i < cl.length; i++) {
toggle(cl[i]);
}
} else {
toggle(cl);
}
};
})();
Not sure if this is the right way to do it, I have been working from other peoples examples
Your invocation of toggle_visibility() in HTML isn't matching code in JS for
if (cl instanceof Array)
isn't true, thus it's toggling all buttons declared with class myContent. cl in function returned from toggle_visibility() is first argument of invocation, which is 'myContent' in your case. But I think you want either use arguments rather than cl there or wrap the list of names in an array on invocation like this:
toggle_visibility( [ 'myContent', 'close_btn', 'open_btn' ] );
But this won't fix your issue nevertheless.
What about click handler like this:
function toggle_visibility(event) {
event.target.style.display = window.getComputedStyle(event.target, null).style == 'none' ? 'block' : 'none';
return false;
}
Note: see https://developer.mozilla.org/en-US/docs/Web/API/event.target
In your HTML you might use
Link
See that example in addition: https://developer.mozilla.org/en-US/docs/Web/API/event.currentTarget
In jQuery you can achieve it in one line...
$(".close-more").on("click",function(){$(this).toggle()})
Related
I have a function which can download free resource and apply or modify my work. I used this function to make my website by setting all divs to display=none and with clicking on a button, the corresponding div display style will become block.
Everything was working fine until I add a music player that the creator use higher jQuery library (3.2.1 > 1.5.2).
Everything works great like before, but when I click on button to play the music, I can't go back or go to other menus.
Debugger error is :
uncaught TypeError: document.getElementById is not a function
But if I don't click on play button, everything is normal.
function openPage(pageName) {
var i;
var x = document.getElementsByClassName("page");
for (i = 0; i < x.length; i++) {
x[i].style.display = "none";
}
document.getElementById(pageName).style.display = "block";
}
Jquery does not prevent vanilla Javascript from running normally.
And as your code is simply Javascript, and from what it appears there are no mistakes within it, it's hard to figure out why such an error would appear. This has nothing to do with Jquery.
The only foreseeable error would be:
pageName is not passed as an argument;
pageName is passed, but is not a string;
no element with the id equal to the pageName's value exists within your document.
You could slightly improve your code by writing it in this way:
function openPage(pageName) {
Array.from(document.getElementsByClassName('page')).map(page => page.style.display = 'none');
document.getElementById(pageName).style.display = "block";
};
You could make it 'more fullproof' by adding some checks:
function openPage(pageName) {
if (pageName && (typeOf pageName === 'string')) {
Array.from(document.getElementsByClassName('page')).map(page => page.style.display = 'none');
var target = document.getElementById(pageName);
if (target) { target.style.display = "block" };
}
};
And yu could improve it by also loggin to the console, when a check has failed, for debugging:
function openPage(pageName) {
if (pageName && (typeOf pageName === 'string')) {
Array.from(document.getElementsByClassName('page')).map(page => page.style.display = 'none');
var target = document.getElementById(pageName);
if (target) {
target.style.display = "block"
} else {
console.log('No element found, with the ID of:' , pageName)
};
} else {
console.log('Error in openPage() : The provided [pagename] argument must be a [string]. Provided value for [pageName] is:', pageName);
}
};
Since .foreach and .map won't work on a nodelist, is the only way to work with the elements in a nodelist through a for loop?
What I'm trying to accomplish is adding different event listeners to the different elements within a nodelist. If the element has the class name of "bold", then the iBold() function should be run, and likewise for "italics" and "underline". Having multiple for loops running to handle each individually feels excessive, so that's why I'm trying to work with one loop to handle all rich text. However, if there's a better way to go about this, I'd really like to know since it seems as though I'm just over-thinking all of this.
var QSA = document.querySelectorAll('div > form > div > a.richText');
for (var rtIndex = 0; rtIndex < QSA.length;rtIndex++) { //Rich text event listeners
var rtid = QSA[rtIndex].id;
var targetiFrame = document.getElementById(rtid).getAttribute('data-pstid');
if (document.getElementById(rtid).className == "richText bold") { //Bold text event listener
QSA[rtIndex].addEventListener('click', function() {
if (targetiFrame != 0) {iBold(targetiFrame);}
else {
document.getElementById('richTextField').contentDocument.execCommand('bold', false, null);
document.getElementById('richTextField').contentWindow.focus();
}
}, false);
} else if (document.getElementsByClassName('richText')[rtIndex].className == 'richText underline') { //Underline text event listener
document.getElementsByClassName('richText')[rtIndex].addEventListener('click', function() {
if (targetiFrame == 0) {
document.getElementById('richTextField').contentDocument.execCommand('underline', false, null);
document.getElementById('richTextField').contentWindow.focus();
} else {iUnderline(targetiFrame);}
}, false);
} else if (document.getElementsByClassName('richText')[rtIndex].className == 'richText italic') { //Italic text event listener
document.getElementsByClassName('richText')[rtIndex].addEventListener('click', function() {
if (targetiFrame == 0) {
document.getElementById('richTextField').contentDocument.execCommand('italic', false, null);
document.getElementById('richTextField').contentWindow.focus();
} else {iItalic(targetiFrame);}
}, false);
}
}
for (var sbmtIndex = 0;sbmtIndex < document.getElementsByClassName('sbmtPost').length;sbmtIndex++) { //Event listener for submitting posts or comments
var iSubmt = document.querySelectorAll('form > div')[sbmtIndex];
document.querySelectorAll('form > div > .sbmtPost')[sbmtIndex].addEventListener('click', function() {
var pstData = iSubmt.querySelector('form > div > .sbmtPost').getAttribute('data-cmtid');
var cPrntID = iSubmt.querySelector('form > div > .sbmtPost').getAttribute('data-pstid');
sendData(pstData, cPrntID); //Post Data (data being the id) and Comment Parent Id. Comments are posts. Variables only used for comments
}, false);
}
If you don't mind converting your NodeList to an Array, you can use:
var nodeArray = Array.prototype.slice.call(nodeList);
Then you can use all the functional methods on the resulting Array.
Note: this is not cross-browser compatible, but neither is .forEach, so I don't think it's an issue.
<div class="btn" data='btn1'>btn1</div>
<div class="btn" data='btn2'>btn2</div>
<div class="btn" data='btn3'>btn3</div>
const btns = document.getElementsByClassName('btn');
function getBtns(el, callback) {
Array.prototype.forEach.call(el, function(node) {
// Wrap below in if() condition here.
node.addEventListener('click', callback);
});
}
getBtns(btns, function() {
console.log(this.getAttribute('data'));
});
this points to the button that's clicked.
I don't know how to do this but I need to hide a repeating (50 times in one page) string of html from being displayed in the browser.
The offending line of html is something like:
<li>Empty</li>
I know this would be a hack but I can't alter the source of this list content.
Is there some javascript code I could put in the head of my document which could hide this string?
Many thanks,
Dar.
You can use :contains to get elements based on their content.
HTML :
<li>Empty</li>
jQuery :
$('a:contains("Empty")').css('display', 'none');
Example : http://jsfiddle.net/9z5du/285
You could hide them without any scripting by setting;
.extLink {
display: none;
}
This will work whether or not JavaScript is enabled and it will mean they won't display even for a flash while the document loads.
If it is specifically the actor links, you can target those too - or if it is both you can make the rule cover both. Let me know if your requirement is more specific.
a[href=actor] {
display: none;
}
If you want to hide them based on the content, you will need to use JavaScript.
var clearEmptyActorLinks = function () {
var externalLinks = document.getElementsByClassName('extLink');
for (var i = 0; i < externalLinks.length; i++) {
if (externalLinks[i].innerHTML === 'Empty') {
externalLinks[i].parentNode.style.display = 'none';
}
}
};
window.onload = clearEmptyActorLinks;
Example: http://jsfiddle.net/ZnK59/
Like this?
window.onload = function () {
var n, hiddens = document.getElementsByClassName('extLink');
for (n = 0; n < hiddens.length; n++) {
if (hiddens[n].innerHTML === 'Empty')
hiddens[n].style.display = 'none';
}
if (hiddens[n].href === 'actor') {
hiddens[n].parentElement.style.display = 'none';
}
}
}
Based on jQuery:
$(function() {
var i = 0;
$('li').each(function() {
if (i == 0) {continue;i++}
$(this).remove();
i++;
});
});
I'm not sure what are you looking for.
Removing empty tags:
$(function() {
var i = 0;
$('li').each(function() {
if (i == 0) {continue;i++}
var $obj = $(this);
if ($obj.find('a').text() == 'Empty') {
$obj.remove();
}
i++;
});
});
I am developing an app that stays in the website itself, and I want every link to call a function. I have tried this:
HTML
link<br>
link 2
Javascript
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].onclick = function () {
return false
}
}
What is wrong? It doesn't work.
Since it's not jQuery, you should use the preventDefault function.
var a = document.getElementsByTagName("a");
for (var i = 0; i < a.length; i++) {
a[i].onclick = function (e) {
e.preventDefault();
doSomething();
}
}
edit for pure javascript solution
document.addEventListener("click", function(e){
if (e.nodeName==="A"){
e.preventDefault();
return false;
}
}, false);
This will only add one single event to the document and prevent all clicks on anchor elements only.
I removed the old solution because of the comment, that this wasn't a jquery question
Don't use return false, it does more than you really need. Instead try event.preventDefault()
var a = document.getElementsByTagName("a").forEach(function (e) {
e.onclick = function (a) {
doSomething(a);
return false;
}
}
}
I have 3 divs with class: wpEdit and onClick: alertName()
<div class="wpEdit" onClick="alertName()">Bruce Lee</div>
<div class="wpEdit" onClick="alertName()">Jackie Chan</div>
<div class="wpEdit" onClick="alertName()">Jet li</div>
When clicked i want to know the Index of class wpEdit of the clicked Div:
function alertName(){
//Something like this
var classIndex = this.className.index; // This obviously dosnt work
alert(classIndex);
}
when clicked on Bruce Lee it should alert : 0
when clicked on Jackie Chan it should alert : 1
when clicked on Jet Li it should alert : 2
I need to know which instance of class="wpEdit" is clicked on
Try this
function clickedClassHandler(name,callback) {
// apply click handler to all elements with matching className
var allElements = document.body.getElementsByTagName("*");
for(var x = 0, len = allElements.length; x < len; x++) {
if(allElements[x].className == name) {
allElements[x].onclick = handleClick;
}
}
function handleClick() {
var elmParent = this.parentNode;
var parentChilds = elmParent.childNodes;
var index = 0;
for(var x = 0; x < parentChilds.length; x++) {
if(parentChilds[x] == this) {
break;
}
if(parentChilds[x].className == name) {
index++;
}
}
callback.call(this,index);
}
}
Usage:
clickedClassHandler("wpEdit",function(index){
// do something with the index
alert(index);
// 'this' refers to the element
// so you could do something with the element itself
this.style.backgroundColor = 'orange';
});
The first thing you might want to address in your code is the inline HTML binding.
You could use document.addEventListener on each element, or rely on event delegation.
The widely most used implementation of event delegation comes with jQuery. If you're already using jQuery, this is the way to go!
Alternatively I've also my own little delegate utility.
const delegate = (fn, selector) => {
return function handler(event) {
const matchingEl = matches(event.target, selector, this);
if(matchingEl != null){
fn.call(matchingEl, event);
}
};
};
const matches = (target, selector, boundElement) => {
if (target === boundElement){
return null;
}
if (target.matches(selector)){
return target;
}
if (target.parentNode){
return matches(target.parentNode, selector, boundElement);
}
return null;
};
This is how you would register the event listener.
document.getElementById('#parent')
.addEventListener('click', delegate(handler, '.wpEdit'));
And this is how you could get the index of the element that generated the event.
const handler = (event) => {
console.log(Array.prototype.indexOf.call(event.currentTarget.children, event.target));
}
Live demo:
const delegate = (fn, selector) => {
return function handler(event) {
const matchingEl = matches(event.target, selector, this);
if (matchingEl != null) {
fn.call(matchingEl, event);
}
};
};
const matches = (target, selector, boundElement) => {
if (target === boundElement) {
return null;
}
if (target.matches(selector)) {
return target;
}
if (target.parentNode) {
return matches(target.parentNode, selector, boundElement);
}
return null;
};
const handler = (event) => {
console.log(Array.prototype.indexOf.call(event.currentTarget.children, event.target));
}
document.getElementById('parent')
.addEventListener('click', delegate(handler, '.wpEdit'));
<div id="parent">
<div class="wpEdit">Bruce Lee</div>
<div class="wpEdit">Jackie Chan</div>
<div class="wpEdit">Jet li</div>
</div>
If you want the index of the div's based on your class wpEdit you can do like this:
HTML:
<div class="wpEdit">Bruce Lee</div>
<div class="wpEdit">Jackie Chan</div>
<div class="other">Other</div>
<div class="wpEdit">Jet li</div>
JS:
$(".wpEdit").bind("click", function(){
var divs = $(".wpEdit");
var curIdx = divs.index($(this));
alert(curIdx);
});
Live example : http://jsfiddle.net/pJwzc/
More information on the index function of jQuery : http://api.jquery.com/index/
Using vanilla javascript, this one works for me:
var wpEdits = document.querySelectorAll(".wpEdit");
for (let i = 0; i < wpEdits.length; i++)
wpEdits[i].addEventListener("click", showID);
function showID(evt) {
for (let i = 0; i < wpEdits.length; i++)
if(wpEdits[i] == evt.target)
alert(i);
}
May not be the best solution though as I am still new to js.
Since I am very new to JS, take the following explanation with a grain of salt:
(Line-1)
This is similar to var wpEdits = document.getElementsByClassName("wpEdit");. It will assign all instances of class="wpEdit" from the html file to the wpEdits variable.
(Line-3 and Line-4)
This two lines will cause any click on the class="wpEdit" to call function showID() defined below.
(Line-6 and Line-10)
When a click event happens, the browser will pass the unique properties of the item being clicked to the evt variable. This then is used in the for loop to compare against all available instances incrementally. The evt.target is used to get to the actual target. Once a match is found, it will alert the user.
To avoid wasting CPU time, running a break; is recommended to exit the loop soon after the match is found.
I could not understand, why people add new functions in previous answers, so...
const wpEdit = document.getElementsByClassName('wpEdit');
for(let i = 0; i < wpEdit.length; i++){
wpEdit[i].addEventListener('click',function(){
alert(i);
});
}
I just added 'click' event, using the loop. And [i] already is the current clicked class index...
FIDDLE