I'm learning ShuffleJS to use it in a project, but I'm running into problems with the search functionality. When I'm trying to search for my items, I keep getting this error:
Cannot read property 'textContent' of null
I grabbed the search code from the docs, but it doesn't seem to work for me.
Here's some code of my search functionality:
HTML
<section class="search">
<div class="container">
<div class="row">
<div class="col-12">
<div class="form-group">
<input type="search" id="search" class="form-control">
</div>
</div>
</div>
</div>
</section>
<section>
<div class="container">
<div class="row" id="grid">
<div class="box green" data-groups='["green"]' data-title="blue">green</div>
<div class="box red" data-groups='["red"]' data-title="red">red</div>
<div class="box green" data-groups='["green"]' data-title="blue">green</div>
<div class="box red" data-groups='["red"]' data-title="red">red</div>
<div class="box green" data-groups='["green"]' data-title="blue">green</div>
<div class="box red" data-groups='["red"]' data-title="red">red</div>
<div class="col-md-2 my-sizer"></div>
</div>
</div>
</section>
JS
const Shuffle = window.Shuffle;
const element = $('#grid');
const sizer = $('.my-sizer');
const shuffle = new Shuffle(element, {
itemSelector: '.box',
sizer
})
$('#search').on('keyup', (e) => {
var searchText = e.target.value.toLowerCase();
shuffle.filter((element, shuffle) => {
var titleElement = element.querySelector('.box');
var titleText = titleElement.textContent.toLowerCase().trim(); // <= this is where the error is thrown
return titleText.indexOf(searchText) !== -1;
});
})
I've also attempted to copy and paste their example JS file here, but I get the same error mentioned above.
I've also reproduced the code written above in a CodePen!
Would anyone know where the problem is? Thanks for the help!
In your filter function, it seems element is already a div with the class box. So there's no need to look within element for an element with class box, as you've already got it.
So you can replace
var titleElement = element.querySelector('.box');
var titleText = titleElement.textContent.toLowerCase().trim(); // <= this is where the error is thrown
with
var titleText = element.textContent.toLowerCase().trim();
Related
This question already has answers here:
Getting the parent div of element
(7 answers)
Closed last month.
I have a project. I am working to find a container using an only child in JavaScript.
I want to add a class to the container of the req-address.
I want to take req in Javascript using an only child of this element. How to do it?
const search = document.querySelector('.search-form');
const addresses = document.querySelectorAll('.req-address');
search.addEventListener('keyup', function(e) {
addresses.forEach(function(address) {
if (address.innerHTML === search.value) {
address.classList.add('.search-active');
}
});
});
<div class="reqs-container">
<div class="req">
<div class="req-time">
<div class="req-time_from">13:00</div>
<span></span>
<div class="req-time_to">15:00</div>
</div>
<div class="req-restaurant">Argentina Grill</div>
<div class="req-address">Оболонь</div>
<div class="req-name">Аліна</div>
Instagram
Приєднатися
</div>
<div class="req">
<div class="req-time">
<div class="req-time_from">13:00</div>
<span></span>
<div class="req-time_to">15:00</div>
</div>
<div class="req-restaurant">Argentina Grill</div>
<div class="req-address">Хрещатик</div>
<div class="req-name">Аліна</div>
Instagram
Приєднатися
</div>
</div>
You needed to remove the dot from the class in .classList.add('.search-active')
To add to the parent div with class = req, you can use
address.closest('div.req')
Here is an alternative version which will toggle instead of just add.
Also I use input event since it handles paste too
Lastly I use includes, textContent, trim and toLowerCase to give the user a better chance to find stuff since innerHTML could have all sorts of whitespace
If you insist on the complete value in the address field must be typed to be found, change
address.textContent.toLowerCase().trim().includes(val)
to
address.textContent === val
const search = document.querySelector('.search-form');
const addresses = document.querySelectorAll('.req-address');
search.addEventListener('input', function(e) {
const val = this.value.toLowerCase();
addresses.forEach(address => address.closest('div.req').classList.toggle('search-active', address.textContent.toLowerCase().trim().includes(val)));
});
.search-active { color: green }
<input type="text" class="search-form" />
<div class="reqs-container">
<div class="req">
<div class="req-time">
<div class="req-time_from">13:00</div>
<span></span>
<div class="req-time_to">15:00</div>
</div>
<div class="req-restaurant">Argentina Grill</div>
<div class="req-address">Оболонь</div>
<div class="req-name">Аліна</div>
Instagram
Приєднатися
</div>
<div class="req">
<div class="req-time">
<div class="req-time_from">13:00</div>
<span></span>
<div class="req-time_to">15:00</div>
</div>
<div class="req-restaurant">Argentina Grill</div>
<div class="req-address">Хрещатик</div>
<div class="req-name">Аліна</div>
Instagram
Приєднатися
</div>
</div>
Basically, I'm asking for a way to optimize this code. I'd like to cut it down to a few lines because it does the same thing for every click bind.
$("#arch-of-triumph-button").click(function(){
$("#arch-of-triumph-info").addClass("active-info")
});
$("#romanian-athenaeum-button").click(function(){
$("#romanian-athenaeum-info").addClass("active-info")
});
$("#palace-of-parliament-button").click(function(){
$("#palace-of-parliament-info").addClass("active-info")
});
Is there a way to maybe store "arch-of-triumph", "romanian-athenaeum", "palace-of-parliament" into an array and pull them out into a click bind? I'm thinking some concatenation maybe?
$("+landmarkName+-button").click(function(){
$("+landmarkName+-info").addClass("active-info")
});
Is something like this even possible?
Thanks in advance for all your answers.
EDIT: Here's the full HTML.
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Arch of Triumph</span>
</div>
<div class="landmark-button" id="arch-of-triumph-button"></div>
</div>
</div>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Romanian Athenaeum</span>
</div>
<div class="landmark-button" id="romanian-athenaeum-button"></div>
</div>
</div>
----------------------------------------------------------
<div class="landmarks-info-wrapper">
<div class="landmark-info" id="arch-of-triumph-info">
<div class="info-landmark section">
<span class="landmark-title">Arch of Triumph</span>
<span class="landmark-coord">44°28′1.99″N 26°4′41.06″E</span>
</div>
</div>
<div class="landmark-info" id="romanian-athenaeum-info">
<div class="info-landmark section">
<span class="landmark-title">The Romanian Athenaeum</span>
<span class="landmark-coord">44.4413°N 26.0973°E</span>
</div>
</div>
Assuming you're not able to modify your HTML markup (in which case with use of CSS classes would be cleaner), a solution to your question would be as shown below:
// Assign same click handler to all buttons
$("#arch-of-triumph-button, #romanian-athenaeum-button, #palace-of-parliament-button")
.click(function() {
// Extract id of clicked button
const id = $(this).attr("id");
// Obtain corresponding info selector from clicked button id by replacing
// last occurrence of "button" pattern with info.
const infoSelector = "#" + id.replace(/button$/gi, "info");
// Add active-info class to selected info element
$(infoSelector).addClass("active-info");
});
Because each .landmark-button looks to be in the same order as its related .landmark-info, you can put both collections into an array, and then when one is clicked, just find the element with the same index in the other array:
const buttons = [...$('.landmark-button')];
const infos = [...$('.landmark-info')];
$(".landmark-button").click(function() {
const i = buttons.indexOf(this);
$(infos[i]).addClass('active-info');
});
This does not rely on IDs at all - feel free to completely remove those from your HTML to declutter, because they don't serve any purpose now that they aren't being used as selectors.
Live snippet:
const buttons = [...$('.landmark-button')];
const infos = [...$('.landmark-info')];
$(".landmark-button").click(function() {
const i = buttons.indexOf(this);
$(infos[i]).addClass('active-info');
});
.active-info {
background-color: yellow;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Arch of Triumph</span>
</div>
<div class="landmark-button" id="arch-of-triumph-button">click</div>
</div>
</div>
<div class="landmark-wrapper">
<div class="page-content landmark">
<div class="heading span-after">
<span>Romanian Athenaeum</span>
</div>
<div class="landmark-button" id="romanian-athenaeum-button">click</div>
</div>
</div>
----------------------------------------------------------
<div class="landmarks-info-wrapper">
<div class="landmark-info" id="arch-of-triumph-info">
<div class="info-landmark section">
<span class="landmark-title">Arch of Triumph</span>
<span class="landmark-coord">44°28′1.99″N 26°4′41.06″E</span>
</div>
</div>
<div class="landmark-info" id="romanian-athenaeum-info">
<div class="info-landmark section">
<span class="landmark-title">The Romanian Athenaeum</span>
<span class="landmark-coord">44.4413°N 26.0973°E</span>
</div>
</div>
Older answer, without knowing the HTML: You can extract the ID of the clicked button, slice off the button part of it, and then select it concatenated with -info:
$(".landmark-button").click(function() {
const infoSel = this.id.slice(0, this.id.length - 6) + 'info';
$(infoSel).addClass('active-info');
});
A much more elegant solution would probably be possible given the HTML, though.
I have the following Html:
<div class="box-row">
<div class="box c2-3">
<div class="box-overlay"></div>
<div class="box-letter"></div>
</div>
<div class="box c2-4">
<div class="box-overlay"></div>
<div class="box-letter"></div>
</div>
<div class="box c2-5">
<div class="box-overlay"></div>
<div class="box-letter"></div>
</div>
<div class="box c2-6 trr">
<div class="box-overlay trr"></div>
<div class="box-letter"></div>
</div>
</div>
I want to randomly select one of the elements with class: c2-3, c2-4, c2-5, c2-6 and trigger a click.
This is the code I have thus far:
var map = [
'c2-3', 'c2-4', 'c2-5', 'c2-6',
];
var x = Math.floor((Math.random() * 4));
var element = document.getElementsByClassName(map[x]);
At this point I want to trigger the click and am unsure how to do it:
element.trigger('click'); ??
Use element.click(); instead of element.trigger('click'); but also, you need to either get only a single element, or loop over the returned HTMLCollection from .getElementsByClassName().
For example, to loop:
var elements = document.getElementsByClassName(map[x])
elements.forEach(element => element.click())
...Or, to get a single element (still using getElementsByClassName):
var element = document.getElementsByClassName(map[x])[0]
element.click()
Alternatively, you can use querySelector:
var element = document.querySelector(`.${map[x]}`)
element.click()
I'm building a menu ordering app where I have to use Jquery.
I'm using the clone() method to duplicate cart items with the necessary data ejected in. It works once, and then logs and empty object with a <prototype>.
What I am cloning is a section in my HTML I am using as a template with an id to keep it hidden. I remove this on the cloned items.
The ejecting of the data I have excluded as it is working fine and the functions are in other files, but I am open to the idea of them being the cause.
HTML:
<div class="cart-item" id="cartitem-template">
<div class="left">
<div class="image"></div>
<div class="price"></div>
</div>
<div class="mid">
<div class="name">
</div>
<div class="stars">
<span></span>
</div>
<div class="underline"></div>
<div class="toggleReviews">
<span></span>
</div>
</div>
<div class="right">
<div class="remove-cart-item">✕</div>
</div>
</div>
</div>
The JS function:
buildCartItem = function(menuItem) {
const template = $("#cartitem-template")
template
.removeAttr("id")
.clone(false)
const newCartItem = template
newCartItem.insertAfter(".cart-item")
console.log(newCartItem)
//Get object and index atts from clicked menu item
//Also set that same data into the dom of the cart item
//As well as set that data to local storage
...
// Apply data to the cloned cart item template
newCartItem.find(".image").css("background-image", "url(" + data.image + ")")
setData(".price", "$"+data.price, newCartItem)
...
}
$(".menu-item").click(function() {
buildCartItem($(this))
})
I'm I using .clone() correctly? Honestly stuck
You are removing the attribute "id" from the source element even before cloning, That's why in the subsequent method invocations it could not find an element with the id "cartitem-template". So in your method buildCartItem, remove "id" after cloning.
const newCartItem = template.clone(false).removeAttr("id");
var buildCartItem = function(menuItem) {
const newCartItem = $("#cartitem-template").clone(false).removeAttr("id");
//newCartItem.find(".image").css("background-image", "url(" + data.image + ")");
//setData(".price", "$" + data.price, newCartItem);
newCartItem.insertAfter("#cartitem-template");
}
$(".menu-item").click(function() {
buildCartItem($(this))
})
#cartitem-template {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="cart-item" id="cartitem-template">
<div class="left">
<div class="image">image</div>
<div class="price">price</div>
</div>
<div class="mid">
<div class="name">name
</div>
<div class="stars">
<span>0</span>
</div>
<div class="underline"></div>
<div class="toggleReviews">
<span></span>
</div>
</div>
<div class="right">
<div class="remove-cart-item">✕</div>
</div>
</div>
<button class="menu-item">Clone</button>
i had similar problem and i solved using function:
function getClone(){
return $(myCloningDiv).clone()
}
I am trying to get first letter of firstname and lastname from a div and paste it in another div but it is pasting the same value in all divs and not taking unique value from each div.
Working Fiddle: https://jsfiddle.net/bv7w8dxg/1/
Issue Fiddle: https://jsfiddle.net/bv7w8dxg/
var takword = $('.nameholder').text().split(' ');
var text = '';
$.each(takword, function () {
text += this.substring(0, 1);
});
$('.avatarholder').text(text);
Markup
`
John Doe
<div class="main-holder">
<div class="nameholder">Kyle Davis</div>
<div class="avatarholder"></div>
</div>
<div class="main-holder">
<div class="nameholder">Seim Seiy</div>
<div class="avatarholder"></div>
</div>
<div class="main-holder">
<div class="nameholder">Momma Boy</div>
<div class="avatarholder"></div>
</div>`
You are using class selectors, which selects all elements with the given class name. That's why you have all the elements set with same value
You need to wrap your elements then process each row independently
I updated your code snippet to demonstrate this:
$('.row').each(function() {
var takword = $('.nameholder', this).text().split(' ');
var text = '';
$.each(takword, function () {
text += this.substring(0, 1);
});
$('.avatarholder', this).text(text);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="row">
<div class="nameholder">John Doe</div>
<div class="avatarholder"></div>
</div>
<div class="row">
<div class="nameholder">Kyle Davis</div>
<div class="avatarholder"></div>
</div>
<div class="row">
<div class="nameholder">Seim Seiy</div>
<div class="avatarholder"></div>
</div>
<div class="row">
<div class="nameholder">Momma Boy</div>
<div class="avatarholder"></div>
</div>