I am creating sliding menus in JavaScript, and the following is my init() function:
function init() {
var menus = new Array();
var allElems = document.getElementsByTagName("*");
for (var i = 0; i < allElems.length; i++) {
alert(allElems[i]);
if (allElems[i].className == "navG") {
alert(allElems[i]);
menus.push(allElems[i]);
}
}
/* assign the openMenu function to the onclick event for each
Menus item */
for (var i = 0; i < menus.length; i++) {
alert(menus[i]);
menus[i].onclick=openMenu;
}
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
}
The problem seems to be in the first for loop. This is definitely the correct class name..just for reference, this is the type of HTML that I am referring to:
<div class="navG" id="gallery1" style="position: absolute; top: 180px; left: -150px; " >
Is there an obvious, or not so obvious reason, that this is not adding the elements to menus?
You got a bug here
document.getElementById("logo").onclick = closeMenu();
document.getElementById("linkList").onclick = closeMenu();
document.getElementById("main").onclick = closeMenu();
You calling closeMenu, not assigning it.
Needs to be
document.getElementById("logo").onclick = closeMenu;
document.getElementById("linkList").onclick = closeMenu;
document.getElementById("main").onclick = closeMenu;
In the page you linked to, in your family.js script, this line:
window.onLoad = init();
says to run the init function immediately and assign its return value to window.onLoad. Because it is running immediately the actual document hasn't been parsed yet so it doesn't find any of your elements. You need to say this:
window.onload = init;
which assigns a reference to the init function to window.onload so that that function will be run later after all of the elements have been parsed.
Also onload should have a lowercase l.
(There are some other problems in your code, e.g., you don't seem to have elements with the ids "linkList" or "main", but I think what I said above is the main problem with the part you are asking about.)
Whoops, you're missing quotes around that "navG" in the first loop.
Related
Apologies if my problem sounds trivial to JS experts.
I've created an image slider (carousel) and, while loading thumbnails, I'm trying to create a reference to a full-size image, so that when a thumbnail is clicked - the image opens in another div.
The relevant code within window.onload handler is:
for (var i = 0; i < numImages; ++i) {
var image = images[i],
frame = document.createElement('div');
frame.className = 'pictureFrame';
/* some styling skipped */
carousel.insertBefore(frame, image);
frame.appendChild(image);
} /* for */
My first attempt was to add "onclick" at the end of the for loop:
frame.onclick= function () {
var largeImage = document.getElementById('largeImage');
largeImage.style.display = 'block';
largeImage.style.width=200+"px";
largeImage.style.height=200+"px";
var url=largeImage.getAttribute('src');
document.getElementById('slides').style.display = 'block';
document.getElementById("slides").innerHTML="<img src='url' />";
}
However, this may only work with hard-coded ids (e.g. 'largeImage').
Ideally, I need to pass image.src as a parameter but this (frame.onclick= function (image.src)) will not work.
My next thought was to put all logic of getting image.src to a separate function and displaying it with frame.onclick= myFunction;
However, I came over an example:
<input type="button" value="Click me" id="elem">
<script>
elem.onclick = function(event) {
// show event type, element and coordinates of the click
alert(event.type + " at " + event.currentTarget);
alert("Coordinates: " + event.clientX + ":" + event.clientY);
};
</script>
And here it is above me to understand why in this example a handler can accept a parameter.
What would be a correct way of assigning an image to the onclick event? Or is there a better way of turning a thumbnail into href?
While it might not be the best way, you could place the full size image path as a data-attribute on your thumbnail.
<img id="thumbnail" src="thumbnailpath" data-fullSizeImage="fullSizePath">
Then on your onclick, you could access the thumbnail element and get it's data- attribute.
function onClick(event){
var fullSizePath = event.currentTarget.getAttribute("data-fullSizeImage");
//Do whatever you want with fullsizepath
}
Code is untested; but, something like that should work based on my experience.
data attributes are a very flexible custom attribute for developers to use. Essentially you start with "data-" and then append a name to represent the attribute. Here is the documententation link.
You can create a closure (that is, an anonymous function) and register it as an event handler to access variables from outside the event handler inside the event handler itself:
for (var i = 0; i < numImages; ++i) {
/* create element */
element.addEventListener('click', function (event) {
console.log('The index is ' + i);
});
}
However, this will not quite work, since the variable i is changed every time the loop increases it, and closures don't "capture" the current value, only the reference itself, so at the end i will be equal to numImages for each event listener.
If you're using ES6 you can overcome this by using let (or const) to prevent this behavior:
for (let i = 0; i < numImages; ++i) {
/* create element */
element.addEventListener('click', function (event) {
console.log('The index is ' + i);
});
}
If using ES6 is not an option, you can still accomplish this in ES5 and earlier by wrapping the inside of your loop in a function that takes i as a parameter, which makes sure each event handler references different variables, since these parameters are different variables for each iteration:
for (var i = 0; i < numImages; ++i) {
(function (index) {
/* create element */
element.addEventListener('click', function (event) {
console.log('The index is ' + index);
});
})(i); /* pass in i here, which will be assigned to the index parameter */
}
Being only able to glue pieces together to get some JS working, I came up with the following code.
if (document.getElementById("component_projector_askforproduct")){
document.getElementById("component_projector_askforproduct").style.display = "none";
}
if (document.getElementById("askforproduct_58676")){
document.getElementById("askforproduct_58676").className = "";
}
if (document.getElementById("longdescription_58676")){
document.getElementById("longdescription_58676").className = "";
}
if (document.getElementById("opinions_58676")){
document.getElementById("opinions_58676").className = "activTab";
}
if (document.getElementById("component_projector_opinions")){
document.getElementById("component_projector_opinions").style.display = "block";
}
if (document.getElementById("component_projector_opinions_add")){
document.getElementById("component_projector_opinions_add").style.display = "block";
}
It works, but I know it's a mess. How could I optimize and slim this code down?
What I would do is:
use object to store all your changes like:
var objChecks = {
component_projector_askforproduct: "some_display_none_className",
askforproduct_58676: "",
longdescription_58676: ""
}
then create function to process it (pass objChecks to it):
function processChecks(checks) {
Object.getOwnPropertyNames(objChecks).map(function(check){
var el = document.getElementById(check);
if (el) el.className=objChecks[check];
})
}
change your HTML a bit. I noticed sometimes you change className and sometimes style.display. I'd make a new class that hides an element (same as display=none) which makes everything much neater.
EDIT I removed my last answer, and got an idea from Mirko Vukušićs answer.
if you create css classes for hiding and showing elements you could do it like this.
css
.hide { display: none!important; }
.show { display: block!important; }
javascript
var arr = [
["component_projector_askforproduct", "hide"],
["askforproduct_58676", ""],
["longdescription_58676", ""],
["opinions_58676", "activTab"],
["component_projector_opinions", "show"],
["component_projector_opinions_add", "show"]
]
for (i = 0; i < arr.length -1; i++) {
var elm = document.getElementById(arr[i][0]);
if(elm) {
elm.className = arr[i][1];
}
}
use ternary conditions for code beautification.
https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/Conditional_Operator
or use switch conditions.
suggestion : If i am in this scenario i would make specific function to perform this task and use ternary condition for code beautification.
To avoid constant checking, and avoid errors, you can do this neat trick, to set default values:
function $(elem) {
return document.getElementById(elem) || document.createElement("div");
}
So, this way, if element doesn't exist, it will be created, but not appended to body!
Test:
function $(elem) {
return document.getElementById(elem) || document.createElement("div");
}
$("component_projector_askforproduct").style.display = "none";
$("askforproduct_58676").className='';
$('longdescription_58676').className="";
$('opinions_58676').className="activeTab";
$("component_projector_opinions").style.display = "block";
//etc, etc....
.activeTab {
background:red;
color:white;
}
<div id="component_projector_askforproduct">
6666666666666
</div>
<div id="opinions_58676">
qqqqqqqqqqqqqq
</div>
<div id="component_projector_opinions7">
11111111111111
</div>
After analyse question code I understand it is some kind of menu or tabs with possibility to activate,deactivate, show, hide elements. I created structure for this purposes with builded in methods. It can be used on many elements, it can be use on one element many times, it has also chain methods so it is possible to run many methods in one code line.
Check code of this structure and examples, if there is some questions or something is not clear then ask. I am not using ES6 or jQuery because this question is not about that.
var ME = function(){
//structure represent single menu element
var MenuElement = function(id){
this.el = document.getElementById(id);
};
MenuElement.prototype.hide = function(){
if (this.el)
this.el.style.display = "none";
return this;
};
MenuElement.prototype.show = function(){
if (this.el)
this.el.style.display = "block";
return this;
};
MenuElement.prototype.active = function(){
if (this.el)
this.el.className = "activeTab";
return this;
};
MenuElement.prototype.deActive = function(){
if (this.el)
this.el.className = "";
return this;
};
//return only obj with create method to use it without new keyword
return {
create: function(id){
return new MenuElement(id);
}
};
}();
//USE EXAMPLE - the same like in question snippet
ME.create("component_projector_askforproduct").hide();
ME.create("askforproduct_58676").deActive();
ME.create("longdescription_58676").deActive();
ME.create("opinions_58676").active();
ME.create("component_projector_opinions").show();
ME.create("component_projector_opinions_add").show();
//USE EXAMPLE MANY TIMES ON ONE ELEMENT - ONLY EXAMPLE PURPOSES
var element = ME.create("component_projector_askforproduct");
var toggle = true;
setInterval(function(){
if (toggle)
element.show().active(); //chaining example
else
element.deActive().hide();
toggle = !toggle;
},1000);
div.activeTab {
font-weight: bold;
color: red;
}
<div id="component_projector_askforproduct">
component_projector_askforproduct
</div>
<div id="askforproduct_58676">
askforproduct_58676
</div>
<div id="longdescription_58676">
longdescription_58676
</div>
<div id="opinions_58676">
opinions_58676
</div>
<div id="component_projector_opinions">
component_projector_opinions
</div>
<div id="component_projector_opinions_add">
component_projector_opinions_add
</div>
Do not call getElementById twice for the same ID. Store return value in a variable, and check that before trying to manipulate it.
I have a simple checkbox.
<div class="myCheckBox"><label><input type="checkbox" checked><span class="label-text">My Label</span></label></div>
Note: The HTML is part of a stack that already has a unique ID assigned to it (referenced the the JS as %id%.
I want to connect it to a javascript function. I know I can use onClick in the HTML to achieve this, but in this use case I need an event handler in my javascript to call (correct term?) the function.
My JS is
$(document).ready(function() {
var stack = $('%id%'),
checkbox = $('.myCheckbox',stack);
function filterCheck(%id=filterName%, %id=values%) {
var sheet = mainViz.getWorkbook().getActiveSheet();
var updateType;
if(checkbox.is(":checked")) {
updateType = "ADD";
} else {
updateType = "REMOVE";
}
worksheetArray = sheet.getWorksheets();
for (var i = 0; i < worksheetArray.length; i++) {
worksheetArray[i].applyFilterAsync(filterName, values, updateType);
}
}
}
checkbox.click(function(e) {
}
});
The event handler (I assume) goes after the checkbox.click(function(e) { ...but I am at a moment of clueless loss. Help?
I could see one issue with the code.
var stack = $('%id%'),
checkbox = $('.myCheckbox',stack);
You are not using # to reference id of element. Correct way should be either escaping the special characters
var stack = $('#\\%id\\%')
or by wrapping up dom object with jQuery Wrapper
var stack = $(document.getElementById('%id%'))
Both will help to find the stack which you have used as context below
$('.myCheckbox',stack);
event handler should work fine as given.
I am working on a dialog script in Vanilla JS. I ran into a problem with the click event on the video image. Even tough the image is surrounded with an anchor tag it shows the image as the event.target on the "trigger-dialog-open" event.
Here is the HMTL:
<a class="trigger-dialog--open thumbnail" data-dialog-id="dialog-video" href="javascript:;">
<figure>
<img src="http://i.ytimg.com/vi/id/sddefault.jpg" alt="" />
</figure>
</a>
And this is the event in JS:
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < openTriggers.length; i++) {
openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
}.bind(this), false);
}
The event handler wants to know the dialog-id from the anchors data attribute. It can't be found because it thinks the image is the event.target, not the actual anchor. How can I correct this? Thanks!
Use event.currentTarget. The event.target is supposed to be the img element since that is what the user has clicked on. The click then bubbles up through the image's containers. event.currentTarget gives you the element that the click handler was actually bound to.
(Or if you didn't bind this to some other object you could use this within the click handler and it should also be the current target.)
I have a few questions is the var openTriggers supposed to be a part of a module hash? Because if it's global then you don't use a this, you only add a this, if it's referencing a variable that the function is also contained in. For example:
var aThing = {
openTriggers: document.getElementsByClassName('trigger-dialog--open'),
openModal: null,
openDialog: function(clickedThingAttr){
if(this.openModal !== null){
this.openModal.style.display = 'none';
}else{
this.openModal = document.getElementById(clickedThingAttr);
}
this.openModal = document.getElementById(clickedThingAttr);
this.openModal.style.display = 'block';
},
setEventListenersNStuff: function(){
for (var i = 0, n = this.openTriggers.length;i < n; i++) {
this.openTriggers[i].addEventListener("click", function (event) {
this.openDialog(event.target.getAttribute('data-dialog-id'));
});
};
}
};//end of aThing hash object
aThing.setEventListenersNStuff();
There are a few issues here:
1. why are you using .bind I think that is a jQuery thing, you want to pass a string to another function when an object is clicked, there no need for binding at all.
2. Also make sure that if you want to do something like open a modal, there is no need to call another method unless it's kinda complex.
3. What about other potential dialogs, it seems that when a .trigger-dialog--open is clicked you're just showing that one one modal with the embedded id, but what about others? Make sure all modals are closed before you open a new one, unless you want to have like 10 modals are open.
A thing to note: I added the line var i = 0, n = openTriggers.length;i < n; i++, now in this case it's silly optimization, and I heard for modern browsers this doesn't apply, but to explain why I added it, is because i < openTriggers.length would count and integrate the array N times. (This may be an outdated optmiziation).
If you meant global
Below I added a different set of code, just in case you meant that var openTriggers is global, kinda like you wrote above. Also I used querySelectorAll for this which is like jQuery's $('.thing') selector.
anyhoo, I also added
var openTriggers = document.querySelectorAll('.trigger-dialog--open');
var n = openTriggers.length;
function openDialog(ddId){
for (var i = 0;i < n; i++) {
openTriggers[i].style.display = 'none';
};
document.getElementById(ddId).style.display = 'block';
};
for (var i = 0;i < n; i++) {
openTriggers[i].addEventListener("click", function (event) {
openDialog(event.target.getAttribute('data-dialog-id'));
});
}
}
So for the question of hiding already open modals I would suggest you could either cache the open Dialog within a module, or you could toggle a class, which would be less efficient since it would require an extra DOM search. Additionally you could add a if this.openModal.id === clickedThingAttr to hide if open, that way you got a toggle feature.
Anyways I suggest you read up on this stuff, if you want to use plain JS but would like the features of jQuery: http://blog.romanliutikov.com/post/63383858003/how-to-forget-about-jquery-and-start-using-native
Thank you for your time.
You can use a closure
var openTriggers = document.getElementsByClassName('trigger-dialog--open');
for (var i = 0; i < this.openTriggers.length; i++) {
(function(element) {
element.addEventListener("click", function (event) {
element.openDialog(event.target.getAttribute('data-dialog-id'));
}, false)
})(openTriggers[i]);
}
Trying to run the following code, but it works either way I need to:
if(my_condition){
$('body').append("<div id='loaddiv'><div>");
for(var i=1; i<=100; i++){
var dl = 0;
$('#loaddiv').load('/script/?p='+i);
dl = $('#loaddiv .standart-view tr').length;
alert(dl);
}
}
Thus, I have to get the number of tr-elements, which is the parent element with class standart-view, and his parents - div block c id = load214235, for each page, but constantly displays zeros in the Alert :( I tried to upgrade the code, adding all the code in the load-callback function, but still does not work ...
if(my_condition){
$('body').append("<div id='loaddiv'><div>");
for(var i=1; i<=100; i++){
var dl = 0;
$('#loaddiv').load('/folder/?p='+i, function(){
dl = $('#loaddiv .standart-view tr').length;
alert(dl);
});
}
}
+I have yet to load after going condition with break; firebug it just swears that break does not know that close ... How do I get out of the situation? I feel that something with a visibility of variables and data do not have time to load another page, and the script is running on. Jquery is connected, the rest of the code is working, including the functions of jquery, the hierarchy of elements on another page observed (number of tr should be).
First load is an Asynchronous call, you are treating it as synchronous. You are reading the content before the Ajax call is returned. Hence why it is always zero.
Second, when you do hook it up write, the content will end up writing on top of each other. You can not use load() and expect it to work in a loop.
Here is the basic idea:
(function () {
var pageCounter = 1;
var tableRowsTotal = 0;
max pageMax=10;
function loadPage() {
$.get('/folder/?p='+pageCounter, function(data){
var table = $(data).filter(".standart-view");
//or
//var table = $(data).find(".standart-view");
var rows = table.find("tbody tr");
var rowCount = rows.length;
console.log(pageCounter, rowCount);
tableRowsTotal += rowCount;
pageCounter++;
if(pageCounter<pageMax) {
loadPage();
} else {
console.log("done");
}
});
}
loadPage();
}());