Javascript Array not working correctly with $.makeArray - javascript

I'm trying to make an array out of a set of div's on a page using jQuery. I basically want to randomise the way they are displayed so for the moment, I'm just trying to cycle through them. Everything appears to work, except that I only ever see the last array item, even though it performs the action the same number of times as there are elements in the array, and adds the correct behaviour.
The JS is:
<script>
$(document).ready(function(){
var obj = $('.item');
var arr = $.makeArray(obj);
$('.array').html('');
$.each(arr, function(k,v){
$('.array').html(v).fadeIn(250).delay(2000).fadeOut(250);
});
});
</script>
And the markup is:
<div class="array">
<div class="item">First</div>
<div class="item">Second</div>
<div class="item">Third</div>
<div class="item">Fourth</div>
</div>
I'm not sure that it's relevant, but here's the CSS, just in case:
div.item {
display: inline; float: left; width: 960px; height: 260px; font-family: helvetica; font-size: 10px; text-align: center; background: #eee; border: solid 1px #888;
}
All I get is the div with the text "Fourth" fading in and out 4 times. This tells me it's iterating through the array fine (as it's using the count) but why am I only seeing the last element? Anyone any thoughts?
Thanks,
T

The loop is overwriting the content of the array div in every iteration of the loop. thus you only see the result of the last iteration.
When you use .html(...) it is the same as .empty().append(...). So what you loop does is empty the content 4 times in a row, and only the append after the last empty will actually take effect.
If you wish the elements to fadein/fadeout one after another you can do it like this:
$(document).ready(function(){
var obj = $('.item');
$('.array').html('');
obj.each(function(i){
$('.array').append($(this).hide().delay(i * 2500).fadeIn(250).delay(2000).fadeOut(250));
});
});
you can see it running here: http://jsfiddle.net/9Xg8X/

Also worth mentioning is that the calls to the effect methods don't block. That is why the loop is already finished before the first effect occurs.
You don't say what you actually want, but if you want to have every element appended and appear/disappear one after another, you can do something like this:
$(document).ready(function(){
var obj = $('.item').detach();
var count = obj.length;
var target = $('.array');
var display = function(i) {
var element = $(obj[i]);
var cb = (i < count-1) ? function(){display(i+1)} : function(){};
element.appendTo(target).hide().fadeIn(250).delay(2000).fadeOut(250, cb);
};
display(0);
});
There is actually no need to use $.makeArray().
DEMO

Related

How to output a specific element value to console.log by clicking a specific element

enter image description here
As shown in the picture above, when the first element value is 7,
If I click, I want to output 7 in console.log.
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(){
//What code should I use?
}
how are you?
Try this:
function clickEvent(evt){
console.log(evt.target.innerHTML)
}
For this you could use the textContent property of HTML elements in JavaScript. To complete this you would also need to pass the event into your clickEvent function. Click events have a event.target, which is the HTML node that was clicked. The event
var container1 = document.createElement('div')
container1.className = 'container1';
document.body.appendChild(container1);
for(var index = 0;index < 20 ; index++){
var aa = document.createElement('span')
aa.innerHTML = card1[index];
container1.appendChild(aa);
aa.className = 'card'+index;
aa.addEventListener('click',clickEvent)
}
function clickEvent(event){
//What code should I use?
let clickedBox = event.target;
console.log(clickedBox.textContent);
}
As far as I know, this should work like you're intending. I haven't worked with the front-end side of JS lately, so correct me if I'm wrong.
You need to capture the click event in your function clickEvent by doing so:
function clickEvent(event){
console.log(event.target.textContent);
}
Capturing the event is done by passing it as an argument while creating your function, do not pass it when you add it to the event listener.
The event is the clicking event, one of its properties is something called target that points to the element that the event click has occurred on, from there you can access anything almost on that element, including its value which I assume you meant its text (the number on it).
If you have provided the HTML and CSS too then it will be a direct answer to your need but a similar approach can be done using this keyword which print innerHTML of each span tag on click
function paret(reed) {
console.log(reed.innerHTML)
}
div {
display: flex;
justify-content: space-between;
}
span {
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
width: 80px;
height: 160px;
background-color: red
}
<div>
<span onclick="paret(this)">7</span>
<span onclick="paret(this)">17</span>
<span onclick="paret(this)">9</span>
<span onclick="paret(this)">8</span>
<span onclick="paret(this)">5</span>
</div>

Using removeChild() properly

I have a function that appends a "div" child into a parent node, then I need to delete this child using the removeChild() method, but it doesn't work.
This is my code:
function ColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="none"
var esempiocolore=document.createElement('div')
esempiocolore.style="position: relative; height: 80px; width: 130px; background-image: url('img/backcard"+cartaesempio+".png'); background-size: cover;"
document.getElementsByClassName("MemoryCards")[i].appendChild(esempiocolore)
}
}
function CleanColorCards()
{for (i=0; i<numerocaselle; i++)
{document.getElementsByClassName("MemoryCards")[i].style.border="dashed 3px #02A494"
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
}
}
Does somebody have any suggestion on how to make it work?
You are passing an NodeList to removeChild, while you should pass a single node. Secondly, document.getElementsByTagName("div") is going to also find elements that are not children of the parent you are trying to remove a child from.
So do it like this:
// Avoid repetition of code, and store the result in a variable:
var nodelist = document.getElementsByClassName("MemoryCards");
for (var i=0; i<numerocaselle; i++){
const parent = nodelist[i];
parent.style.border="dashed 3px #02A494";
// Perform `getElementsByTagName` on the parent node only, and take first match:
parent.removeChild(parent.getElementsByTagName("div")[0]);
}
Note that querySelector is designed for getting one node-result, so that last line can read:
parent.removeChild(parent.querySelector("div"));
Just a couple notes:
Using a for loop is unnecessary. Having a variable to hold the count of the length of .MemoryCards will leave room for errors. Instead, I recommend an Array function such as .forEach() to iterate through your elements.
The bulk of your element styles should be handled with classes in CSS. By doing this your function will be more concise and easier to manage.
And, to answer your question:
To remove all child nodes for each .MemoryCards, I would recommend using a loop and the node.removeChild() method as it will perform faster than setting node.innerHTML=''.
See the comments in this post as why this method would be best.
let cartaesempio = 10;
ColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('borderNone');
let esempiocolore = document.createElement('div');
esempiocolore.style.backgroundImage = `url('https://cdn.dribbble.com/users/285803/screenshots/1066705/dribbble.gif')`;
e.appendChild(esempiocolore);
});
CleanColorCards = () =>
Array.from(document.getElementsByClassName("MemoryCards"))
.forEach(e => {
e.classList.add('boderDashed');
while (e.firstChild) {
e.removeChild(e.firstChild);
}
});
ColorCards();
CleanColorCards();
/* Children of the .MemoryCards nodes */
.MemoryCards div {
position: relative;
height: 80px;
width: 130px;
background-size: cover;
}
.borderNone {
border: none;
}
.boderDashed {
border: dashed 3px #02A494;
}
<div class='MemoryCards'></div>
Hope this helps,
getElementsByTagName returns node list(array). You will have to select a node. Maybe something like this would be useful for you:
document.getElementsByTagName("div")[0]
https://developer.mozilla.org/en-US/docs/Web/API/Element/getElementsByTagName
just check your error ! document.getElementsByTagName("div") return an array of div that's basic js meaning you've to search more by ourself.
Use a manual, like the one at w3schools or a book whatever :-)
You are passing removeChild all the divs in the document.
Try replacing this line:
document.getElementsByClassName("MemoryCards")[i].removeChild(document.getElementsByTagName("div"))
With:
var memoryCardsEl = document.getElementsByClassName("MemoryCards")[i];
memoryCardsEl.removeChild(memoryCardsEl.getElementsByTagName("div")[0]);

How to get column number based on text

I've divided the Html content (which belongs to an eBook) into multiple columns using the following steps.
1) I've added the HTML inside the content which is inside a container.
<body>
<div id="container">
<div id="content">
BOOK HTML CONTENT
<span id="endMarker"></span>
</div>
</div>
</body>
2) Next, I've added the CSS style of the content and the container as shown below:
#container {
width: 240px;
overflow: hidden;
background-color: yellow;
}
#content {
position: relative;
height: 30em;
-moz-column-width: 240px;
-webkit-column-width: 240px;
column-width: 240px;
-moz-column-gap: 10px;
-webkit-column-gap: 10px;
column-gap: 10px;
}
Now, I want to find the column number of the text (or a line) using javascript?
There are other questions on SO that show how to get the column number based on the id. In my case, there are no id's. The only thing available is the text (or line) and I need to get the column number by searching through the Html content.
Currently, I've two "solutions" to get the column number but they are incomplete.
1) I can find whether the text exists or not by using window.find(text) after that I'm not sure what I've to do.
2) Another option is to add <span> with an id to every line temporarily and remove it. Once added, I can get the total column count up to that line (like shown below).
columnCount = Math.floor($('#marker').position().left/(columnWidth + columnGap));
This will give a wrong number if the line is extended to another column.
The second solution is tricky and book content is huge. I don't think this is the best way to get the column number. I'm looking for a simpler solution.
Try this, made a workable version for your question.
jsfiddle link
Although OP didn't tag question with jQuery, but actually used jQuery inside the question, I use it too for cleaner code. (and fit the question)
What I do in this example:
Make a long content cross several pages to visualize paging (with css: column-width).
Click on previous / next to browse pages.
Input and click 'find' button, make found texts highlighted.
List all columns (pages) found with input text.
Click on the link and jump to column with searched text.
In detail I made temporary DOM elements to calculate column, and remove them right after to keep DOM tree clean.
2017/6/1 Edited: Added highlight color for searched text.
$('#find').click( () => {
var text = $('#findtext').val();
var columns = [];
var doms = [];
while (window.find(text, false)) {
var sel = window.getSelection();
if (sel) {
var range = sel.getRangeAt(0);
var el = document.createElement("span");
var frag = document.createDocumentFragment();
frag.appendChild(el);
range.insertNode(frag);
columns.push(Math.floor(el.offsetLeft/(_columnWidth + _columnGap)));
doms.push(el);
}
}
/// distinct
columns = columns.filter( (value, index, self) => self.indexOf(value) === index );
/// show result
$("#foundlist").empty();
for (var i=0; i<columns.length; i++)
$("#foundlist").append(`<li>Found in Column ${columns[i]+1}</li>`);
/// remove dom. keep dom tree clean
while (doms.length > 0) {
var dom = doms.pop();
dom.parentNode.removeChild(dom);
}
});
Instead of adding a <span> beforehand, you could temporarily insert it at the point where you find your text and remove it again as soon as you have identified the position.
The key is how to find text in a long document. The interface for this task is the TreeWalker, that can iterate through every text node in a DOM subtree.
How to insert an element in the middle of a text node was copied from here.
Your question did not state it, but used jQuery as a dependency. This solution is using only the vanilla DOM interfaces.
var columnWidth = 240,
columnGap = 10;
function getColumn(text) {
// the subtree to search in
var root = document.getElementById('content');
// define an iterator that only searches in text nodes
var treeWalker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT, {
// filter the text nodes to those containing the search text
acceptNode: function(node) {
if ( node.data.includes(text) ) {
return NodeFilter.FILTER_ACCEPT;
}
}
});
// look if there is a result
if (treeWalker.nextNode()) {
var node = treeWalker.currentNode;
// get start index of found text
var index = node.data.indexOf(text);
// and split into two nodes, referencing the second part
var foundTextNode = node.splitText(index);
// define an empty inline element
var span = document.createElement('span');
// insert it between the two text nodes
var elem = node.parentElement;
elem.insertBefore(span, foundTextNode);
// compute the column from the position of the element
// you might have to account for margins here
var x = span.getBoundingClientRect().left - root.getBoundingClientRect().left;
var column = Math.floor(x / (columnWidth + columnGap));
// restore previous state
elem.removeChild(span);
elem.normalize();
return column;
} else {
// no result
return false;
}
}
The obvious limitation to this solution is that it will not find text that is spanning multiple nodes. So, if you have a text fragment
<p>The quick brown fox <em>jumps</em> over the lazy dog.</p>
a search for 'fox jumps over' will not find this sentence.

How can I access all elements within a NodeList?

With pure JavaScript, I've used this line to return a NodeList of multiple elements:
var elements = document.getElementsByClassName('icon-wrapper');
I can then use any of these lines to target a specific index from that NodeList:
elements[0];
elements[1];
elements[2];
But when I try to include multiple objects from the NodeList or loop through them, only one node, the last one requested, is returned.
I'm trying to apply .appendChild to all nodes with the class of icon-wrapper
Here is the code:
var square = document.createElement('div');
square.style.width = "32px";
square.style.height = "32px";
square.style.zIndex = "-1";
square.style.position = "absolute";
square.style.backgroundColor = "red";
var elements = document.getElementsByClassName('icon-wrapper');
var requiredElement = elements[0, 1, 2];
requiredElement.appendChild(square);
body {
background: lightyellow;
}
.icon-wrapper {
width: 30px;
height: 30px;
background: green;
float: left;
margin: 2px;
}
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
Are you trying to achieve something like this? Check out :
https://jsfiddle.net/cd2gbuc9/3/
i.e:
1) Get the required elements.
For each of them in a loop:
1) Create a square
2) Append the square to the element
There can be very different ways to achieve this. Easy ways create 3 different variable for square to be added. and then append them each using a for loop to each element.
when you use getElementsByClassName result is "NodeList". the NodeList is a array of "node" and node can be "element" or "attribute" or "comment" and or "text".
you most check noteType in loop and only add elemnt to node type element that nodeType is 1.
for(var i = 0; i < elements.length; i++)
{
if(elements[i].nodeType === 1){
elements[i].appendChild(square);
}
}
for more information see this reference:
http://www.w3schools.com/jsref/prop_node_nodetype.asp
var requiredElement = elements[0,1,2];
This won't do what you think it does, whatever that is. It is equivalent to var requiredElement = elements[2];.
You need to get out of your jQuery mindset. You could start by not referring to "pure JavaScript"; all JavaScript is pure. DOM elements are not jQuery-like magical groups of things that you can simultaneously apply classes to, or append all of to something or append something to all of, or whatever. You will have to LOOP over a list resulting from something like getElementsByClassName and do whatever you want element-by-element.
In general, the use of getElementsByClassName itself is a code smell. Do not use classes as way to identify elements that you will then manipulate and insert and delete. Classes are a CSS construct, whose main purpose in life is to associate elements with CSS rules whose selectors specify those classes.
For example, instead of creating a div in code with laboriously assigned individual styles, and then trying to insert it here and there with code (hint: you can only include it in one place; if you insert it somewhere else it will MOVE there and be removed from where it was before), just add it to your HTML, with a class which brings in all those styles.
Your logic is in error in two ways:
The statement elements[0,1,2] is equivalent to elements[2]; it's only returning the last element, not all of them.
You're trying to add the same ONE square to multiple elements. Even without error number 1, it would end up on the last element anyway, because there is only one square to go around. If you want one square on every element in the NodeList, you need to create one square for every element in the NodeList, and then loop through it, appending a square on each iteration. For example:
Convert the NodeList to an Array:
var node_list_array = Array.prototype.slice.call(div_list);
and append a different square to each element:
node_list_array.forEach(function(elem, index){
elem.appendChild (squares[index]);
});
First, Document.getElementsByClassName. This is something to be aware of if you are removing/adding elements during a loop, as it is a live HTMLCollection.
var requiredElement = elements[0,1,2]; is not a way to access indexed items from an array like object, I don't know where you got that idea.
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
So you need to iterate array like objects, by using iteration methods.
You need to create a new red box for each appendChild, otherwise you will just move the box from one place to another.
An example written in ES6.
function createARedSquare() {
const square = document.createElement('div');
// use CSS for a class when possibe
square.className = 'redSquare';
return square;
}
//const slice = Function.prototype.call.bind(Array.prototype.slice);
// Gets a 'live' HTMLCollection and converts to an Array, a fixed node content
function getElementsByClassName(className) {
// You could use Array#slice here instead of Array.from if < ES6
return Array.from(document.getElementsByClassName(className));
}
function addRedSquareToEachIconWrapper() {
getElementsByClassName('icon-wrapper')
.forEach(iconWrapper => iconWrapper.appendChild(createARedSquare()));
}
addRedSquareToEachIconWrapper();
body {
background: lightyellow;
}
.icon-wrapper {
width: 30px;
height: 30px;
background: green;
float: left;
margin: 2px;
}
.redSquare {
width: 32px;
height: 32px;
background: red;
z-index: -1;
position: absolute;
}
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>
<div class="icon-wrapper">
<svg></svg>
</div>

How do I repeat div classes using JavaScript only?

Okay, I'm unsure how to word the question, but basically I want to repeat my div containers that have a class of "blocks" using only javascript, no HTML (other than the HTML needed to start a page). IF I were doing this using HTML the result should look exactly like this.
http://jsfiddle.net/nqZjB/1/
<div class = "blocks"> <!-- Repeats three times -->
However as I stated in the description I do not want to use any HTML, so here is my fiddle with javascript only.
How do I make div class blocks repeat three times as in my HTML example using only javascript? Of course in real life I would use HTML for this as javascript is unnecessary, but I want to do this in pure javascript so I can learn. Also as a sidenote if you have a better way as to how I should have worded the question, let me know.
Thanks (:
http://jsfiddle.net/TbCYH/1/
It's good you see the use of making a function of a re-occurring pattern.
Before posting it in StackOverflow, have you tried doing it yourself?
jsfiddle: http://jsfiddle.net/kychan/W7Jxu/
// we will use a container to place our blocks.
// fetch the element by id and store it in a variable.
var container = document.getElementById('container');
function block(mClass, html) {
//extra html you want to store.
return '<div class="' + mClass + '">' + html + '</div>';
}
// code that loops and makes the blocks.
// first part: creates var i
// second: condition, if 'i' is still smaller than three, then loop.
// third part: increment i by 1;
for (var i = 0; i < 3; i++) {
// append the result of function 'block()' to the innerHTML
// of the container.
container.innerHTML += block('block', 'data');
}
Edit: JS has changed a lot since the original post. If you do not require compatibility, use const, template literals, class and querySelector to make the code a bit cleaner. The following code has a Builder class and assumes there is a div with ID 'container':
// create class builder.
class Builder {
// create constructor, accept an element selector, i.e #container.
constructor(targetContainerSelector) {
// search element by given selector and store it as a property.
this.targetContainer = document.querySelector(targetContainerSelector);
}
// method to append to innerHtml of target container.
appendUsingInnerHtml(divAsHtml) {
this.targetContainer.innerHTML += divAsHtml;
}
// method to append to target container using DOM elements.
appendUsingDom(divAsDom) {
this.targetContainer.appendChild(divAsDom);
}
}
// constant to hold element selector.
const myTargetContainer = '#container';
// constant to set the class if required.
const myDivClass = 'my-class';
// constant to hold the instantiated Builder object.
const builder = new Builder(myTargetContainer);
// loop 3 times.
for (let i=0; i<3; i++) {
// call method to append to target container using innerHtml.
builder.appendUsingInnerHtml(`<div class="${myDivClass}}">innerhtml div text</div>`);
// OR.. build using DOM objects.
// create the div element.
const div = document.createElement('div');
// create text element, add some text to it and append it to created div.
div.appendChild(document.createTextNode('dom div text'));
// call method to append div DOM object to target container.
builder.appendUsingDom(div);
}
Please note: Every time something is added to the DOM, it forces the browser to reflow the DOM (computation of element's position and geometry).
Adding everything at once, improve speed, efficiency and performance of a code.
(ref: document.createDocumentFragment)
window.onload = Create();
function Create() {
// create the container
var mainContainer = document.createElement('div');
mainContainer.id = 'mainContainer';
// add all style in one go
mainContainer.setAttribute('style', 'witdht: 400px; height: 200px; border: 2px solid green; margin-left: 20px;');
var divBlocks1 = document.createElement('div');
divBlocks1.className = 'blocks';
divBlocks1.setAttribute('style', 'width: 100px; heigth: 100px; border: 1px solid black; margin-left: 20px; margin-top: 10px; floar: left;');
var divBlocks2 = divBlocks1.cloneNode(false); // copy/clone above div
var divBlocks3 = divBlocks1.cloneNode(false); // copy/clone above div
// everything is still in memory
mainContainer.appendChild(divBlocks1);
mainContainer.appendChild(divBlocks2);
mainContainer.appendChild(divBlocks3);
// now we append everything to the document
document.body.appendChild(mainContainer);
}
Good luck
:)
for(var d=0;d<10;d++){
var aDiv = document.createElement('div');
aDiv.className = "block";
document.getElementsByTagName('body')[0].appendChild(aDiv);
}
Rather than creating the elements before hand and then appending them to the main container, consider dynamically creating and appending them in a loop.
http://jsfiddle.net/TbCYH/6/
for(var i = 0; i < 3; i++) {
var divBlock = document.createElement("div");
divBlock.className = "blocks";
mainContainer.appendChild(divBlock);
}
In the above code snippet a div is being created and appended for each iteration of the loop (which is set to cease at 3).
Also if possible, always use CSS classes rather than modifying the styles for each div directly.

Categories

Resources