How to use querySelector when manipulating elements with classes that contain multiple words? - addeventlistener

I'm trying to addEventListener that will listen for "click"s and will return an alert.
Is it class name or something else that creates the problem?
Couldn't figure out what am I doing wrong in this code:
HTML:
<button class="w drum">w</button>
Javascript:
var wDrum = document.querySelector(".w drum");
wDrum.addEventListener('click', 'alert("click!")');

All you're missing is changing the selector to .w.drum to find both classes on the same element:
var wDrum = document.querySelector(".w.drum");

Just giving you hint how to make workaround.
#pavlindrom answer is the shortest way to go around and should be a way to go.
But suppose you don't want to go around with '.w.drum' then my friend you need to get learned about CSS Selector Reference
Among many ways [attribute=value] is a way to target class attribute on the button. This is probabaly what query selector is doing behind the scene for '.w.drum'.
So in our case it would become "[class*=w drum]". '*=' part means constains substring in given string.
Example with attribute searching : side note will throw error.
var wDrum = document.querySelector("[class*=w drum]");
wDrum.addEventListener('click', () => {alert("click!")})
<button class="w drum">w</button>
Error is genrated becuase of space between w drum in [class*=w drum] JS engine doesn't really try too hard to make sense of things on its own.
So we have to tell it by explicitly telling it what mean, by using quotes. thus "[class*=w drum]" becomes "[class*='w drum']"
Updated Example: it works
var wDrum = document.querySelector("[class*='w drum']");
wDrum.addEventListener('click', () => {alert("click!")})
<button class="w drum">w</button>
If you keep the order of words right, it can work fine.
var wDrum = document.querySelector("[class*='w drum']");
wDrum.addEventListener('click', () => {alert("click!")})
<button class=" another-class w drum some-other-class">w</button>
Explainer in connection with your comment
.class1 .class2 With space in between classes selects all elements i.e. class 2 that are descendant of an element with class1
For example;
<div class='class1'>
<span class='class2'></span>
</div>
.class1.class2 with out any space searches for all elements with both class1 and class2 set within its class attribute
For example;
<div class='class1 class2'>
<span class='class2'></span>
</div>
document.querySelector(".w.drum") would return div tag because both classes are present in same tag attribute.
Why did the arrow function work?
addEventListener function expects two arguments ie.
.addEventListener(argument1, argument2)
argument1 must be a string referencing particular event type.
argument2 must be a call back function or at least reference to a already declared function.
For example
.addEventListener('click', function_name)
or as in direct declaration of function
.addEventListener('click', function () {})
or as in arrow function
.addEventListener('click', () => {})
Your code had argument2 as string so it didn't work
wDrum.addEventListener('click', 'alert("click!")')
but if we change it to function it works because both are executable functions
wDrum.addEventListener('click', () => {alert("click!")})
or
wDrum.addEventListener('click', function () {alert("click!")})
further gotcha following code would trigger alert the moment JS engine reads the code, so we had to excapsulate it into a parent function.
wDrum.addEventListener('click', alert("click!"))
var wDrum = document.querySelector(".w.drum");
wDrum.addEventListener('click', alert("click!"));
wDrum.addEventListener('mouseover', () => alert("hover!"));
<button class=" another-class w drum some-other-class">w</button>
alert, console.log, or document.querySelector etc are self triggering functions they execute moment code is run and do not wait for your interaction unless they are scoped into a parent function which is triggered on call back through your interaction.

Related

Svelte input binding breaks when a reactive value is a reference type?

(I'm new to Svelte so it is quite likely that I'm doing something wrong here)
UPDATE: I've added a second, slightly different REPL which may demonstrate the problem better. Try this one: https://svelte.dev/repl/ad7a65894f8440ad9081102946472544?version=3.20.1
I've encountered a problem attempting to bind a text input to a reactive value.
I'm struggling to describe the problem in words, so hopefully a reduced demo of the issue in the attached REPL will make more sense.
https://svelte.dev/repl/6c8068ed4cc048919f71d87f9d020696?version=3.20.1
The demo contains two custom <Selector> components on a page.
The first component is passed two string values ("one" and "two"):
<Selector valueOne="one" valueTwo="two"/>
Clicking the buttons next to the input field sets selectedValue to one of these values.
This, in turn, triggers the following reactive declaration to update:
$: value = selectedValue
The input field is bound to this reactive value:
<input type="text" bind:value>
So clicking the "One" button sets the input text to "one", and clicking the "Two" button sets the input field to "two".
Importantly though, you can still type anything into the input field.
The second component is passed two array values:
<Selector valueOne={[1, "one"]} valueTwo={[2, "two"]}/>
Again, clicking the buttons sets selectedValue to one of these.
However this time the reactive declaration depends on an array element:
$: value = selectedValue[1]
Everything works as before, except now you can no longer type into the input field at all.
So the question is - why does <input bind:value> behave differently for these two:
$: value = aString
vs
$: value = anArray[x]
It seems that this is only an issue when using two-way bindings.
By switching to a one-way and an on:input handler, the problem goes away:
i.e. instead of this:
<input type="text" bind:value={valX}/>
use this:
<input type="text" value={valX} on:input={e => valX = e.target.value}/>
I'm pretty sure your reactive declaration is overwriting your bound value as soon as it changes, which is with every key stroke on the input and every button press. Meaning it technically is working, you're just reverting it each time it changes. Check out this version of it that uses a watcher.
Also binding to a reactive declaration means you're never actually changing the variables with the input (which you can see in your JSON result on the first selector when you type in the input the value doesn't update only on button click).
Why not lose the reactive declaration and bind directly to the variable you want. Then use an {#if} block to switch between which version of the input you're showing based on the truthiness of index?
<script>
export let valueOne;
export let valueTwo;
export let index;
let selectedValue = index? [] : '';
let selectValue = (val) => selectedValue = val;
</script>
{#if index}
<input type="text" bind:value={selectedValue[index]} placeholder="Type anything...">
{:else}
<input type="text" bind:value={selectedValue} placeholder="Type anything...">
{/if}
<button on:click={() => selectValue(valueOne)}>One</button>
<button on:click={() => selectValue(valueTwo)}>Two</button>
<p>
<strong>Selected value:</strong> {JSON.stringify(selectedValue)}
</p>
By binding directly to the selectedValue or an index of it you have the added benefit of changing the value with the input. Here's a working example in the REPL

New to React: Why is one array treated differently than the other?

I'm working on a React app that is fed data from a Rails api. I'm currently working on a form that includes a nested association (i.e. in the model_a has many model_b's and you can create them in the same form).
The problem I'm having is that Rails expects nested association with a certain naming convention and the same field that controls how the parameter is named when its sent to rails also controls how React finds the right data when the Rails API responds.
This becomes problematic on the edit page because I want to show the models_a's (Retailers) already existing model_b's (SpendingThresholds in this case) and when I change the 'name' field to suit the rails side, React doesn't know where to look for that data anymore. When I try to pass the data directly it comes in as a different type of array and certain functions fail.
I think its easier to show than tell here so
initially I had this
<FieldArray
name="spending_thresholds"
component={renderSpendingThresholds}
/>
and data was coming through like
Object {_isFieldArray: true, forEach: function, get: function, getAll: function, insert: function…
to my React app from the Rails API, which worked, however that 'name' isn't to Rails liking (Rails wants it to be called 'spending_thresholds_attributes' for accepts_nested_attributes to work) so I changed it to
<FieldArray
name="spending_thresholds_attributes"
fields={this.props.retailer.spending_thresholds}
component={renderSpendingThresholds}
/>
and data start coming through to the renderSpendingThresholds component in this format
[Object]
0:Object
length:1
__proto__:Array(0)
which React doesn't like for some reason.
Anyone know how to fix this/why those two objects, which hold the same information from the Rails side anyway, are being treated differently?
EDITS
renderSpendingThresholds component
The fields attribute in the renderSpendingThresholds component is the object that's coming through differently depending on how I input it
const renderSpendingThresholds = ({ fields }) => (
<ul className="spending-thresholds">
<li>
<Button size="sm" color="secondary" onClick={(e) => {
fields.push({});
e.preventDefault();
}
}>
Add Spending Threshold
</Button>
</li>
{fields.map((spending_threshold, index) => (
<li key={index}>
<h4>Spending Threshold #{index + 1}</h4>
<Button
size="sm"
color="danger"
title="Remove Spending Threshold"
onClick={() => fields.remove(index)}
>
Remove
</Button>
<Field
name={`${spending_threshold}.spend_amount`}
type="number"
component={renderField}
label="Spend Amount"
placeholder="0"
/>
<Field
name={`${spending_threshold}.bonus_credits`}
type="number"
component={renderField}
label="Bonus Credits"
placeholder="0"
/>
</li>
))}
</ul>
);
It looks like you are passing fields through props and then destructuring the fields out of the props in the callback of the renderSpendingThresholds and discarding the rest. According to the docs, a specific redux-form object is passed through to the render callback. You're essentially overwriting this. Try changing {field} to something like member or spending_threshold. Then you can use the specific map function to iterate over the spending_threshold items. Your field prop should still be available under member.fields or something similar.
For the code that you currently show, who exactly handles the submission?
you use the original flow of form submit?
if so, so please handle that by yourself.
** this line of code, looks weird:
onClick={() => fields.remove(index)}
as you interact directly with the state values...
you need to update the state through
this.setState({fields: FIELDS_WITHOUT_ITEM})
and now when you need to handle your own submission, you don't really care of the input names. Because you are using the state as input.
ie:
class FormSpending extends Component {
handleSubmit() {
var fieldsData = this.state.fields.map(field => {
return {
whateverkey: field.dontcare,
otherKey: field.anotherDontCare
};
});
var formData = {
fields: fieldsData
};
ajaxLibrary.post(URL_HERE, formData).....
}
render() {
return (
...
<form onSubmit={()=>this.handleSubmit()}>
...
</form>
...
);
}
}

Determining the type of a standard jQueryUI widget

I am having trouble determining the type of a given jQueryUI widget instance.
The jQueryUI documentation for the Widget Factory suggests two techniques. From the "Instance" section:
The widget's instance can be retrieved from a given element using the
instance() method. [...]
If the instance() method is called on an element that is not
associated with the widget, undefined is returned.
the :data selector can also determine whether an element has a given
widget bound to it.
Based on their examples, let's say I initialize a datepicker and later code checks if it is a datepicker:
<p>Date: <input type="text" id="datepicker"> </p>
$(function() {
$("#datepicker").datepicker();
// ...
var i = $("#datepicker").progressbar("instance"); // i is undefined as expected
console.log(i);
var b = $("#datepicker").is(":data('ui-datepicker')"); // b = false, not sure why
console.log(b);
var i2 = $("#datepicker").datepicker("instance"); // this throws an exception
console.log(i2);
});
Based on the documentation I expected the .is call to return true, and the last line to return the instance (not throw an exception.)
JSFiddle is here. (You will need to open the browser's console to see the logged output.)
It turns out the techniques I listed above do work for many jQueryUI widgets, e.g. button, progressbar.
But datepicker is kind of weird. Looking at the DOM after a datepicker is initialized, I see the datepicker is inserted as a new element after the named element.
To get the datepicker widget instance I'd need to navigate the DOM starting from the named element. To check if the input field has a datepicker on it we can simply check the element for the class hasDatepicker:
var isDatePicker = $("#datepicker").is(".hasDatepicker");
This works with jQueryUI 1.11.2, and based on other SO questions it's been working since 2009. So I guess it's a reliable technique, but I'm not sure if its documented anywhere, or guaranteed for future versions.

Get children of an XHPChild

I am trying to move my website to Hack and XHP, of course. Below is a structure of what code structure I want to achieve:
<ui:backstageHeader>
<ui:backstageHeader-navItem href="/">stories</ui:backstageHeader-navItem>
<ui:backstageHeader-navItem href="/story/send">send a story</ui:backstageHeader-navItem>
<ui:backstageHeader-navItem href="/aboutus">support</ui:backstageHeader-navItem>
</ui:backstageHeader>
(Note: :ui:backstageHeader-navItem basically renders to <a href={$this->:href}>{$this->getCHildren}</a> so there is not need to attach its class here.)
Below is the code for :ui:backstageHeader:
final class :ui:backstageHeader extends :ui:base {
attribute :div;
children (:ui:backstageHeader-navItem)*;
protected function compose() {
$dom =
<section class="backstage-header">
<div class="container">
<div class="cell-logo">
<a href="/">
<span class="no23-logo-white"></span>
</a>
</div>
<div class="cell-navigation">
</div>
<div class="cell-account">
<div class="cell-login">
<div id="siteNav-login">Autentificare</div>
</div>
</div>
</div>
</section>;
$mainContainer = $dom->getChildren("div")[0];
$cellNavigation = $mainContainer->getChildren("div")[1];
$navItems = <ul class="main-navigation"></ul>;
foreach($this->getChildren() as $child) {
$navItems->appendChild(<li>{$child}</li>);
}
$dom->appendChild($navItems);
return $dom;
}
}
I used the Terminal to debug my code using hhvm -m d <file.php>, and everything was alright there; however, when I get to my browser, I get 500 error header. This is what the log says:
Catchable fatal error: Hack type error: Could not find method getChildren in an object of type XHPChild at /var/www/res/ui/backstage-header.php line 25
The error comes from
$cellNavigation = $mainContainer->getChildren("div")[1];
But, somehow, I need to append ul.main-navigation to div.cell-navigation from my section.backstage-header.
How can I do it?
Don't structure your code this way. Built it up from the inside out, so that you don't have to do a ton of unreadable getChildren calls looking for specific children. Those calls are super hard to read, and super inflexible when you change the structure of your XHP. You wouldn't do something like node.firstChild.lastChild.lastChild.firstChild in the JS DOM, would you? No, there's a better way in JS, to find things by class or ID; in XHP, you can just build it up the right way in the first place!
I'd give you an example of this, but it doesn't look like you actually use $mainContainer or $cellNavigation, so you can just remove those two problematic definitions.
As an aside, you really shouldn't be getting your type errors as catchable fatals from HHVM; this is a last resort sort of check. Try running the hh_client checker directly, maybe even showing its result in your IDE; it will give you a much faster iteration cycle, and much more information than HHVM provides.
From my experience, appendChild is very prone to human error. It's easier to do something like:
$items = (new Vector($this->getChildren()))->map($child ==> <li>{$child}</li>);
return <div id="container">{$items}</div>;
If you want to wrap the children in <li />.
Not sure if that will work but it will be close.
Pro tip: You can assign variables from within an XHP tree.
$root =
<div>
{$child = <span>
Text children
</span>}
</div>;
Now $child is already set to the <span> element.

Razor syntax isn't recognizing '#' after an underscore

I am trying to do the following:
#foreach(var p in #Model.line_products){
<img class="small_img_#Model.line_products[i].short_name" src="/Content/images/#Model.line_products[i].image_name" />
}
Which isn't working, it renders the text just the way it is, not recognizing the '#'. I found this other post in Stackoverflow, which suggests adding parenthesis in the following way:
#foreach(var p in #Model.line_products){
<img class="small_img_(#Model.line_products[i].short_name)" src="/Content/images/#Model.line_products[i].image_name" />
}
Using this workaround, I get that my id is rendered as small_img_(MODEL ATTRIBUTE). Isn't there a workaround which doesn't require adding specific characters? (such as the parenthesis).
You have more errors than a simple undercore problem here. You cannot use #Model inside your if. You are already in a # block. Simply use #foreach(var p in Model.line_products).
Plus, the way you wrote the parenthesis, they will get rendered. What you want is
small_img_#(Model.line_products[i].short_name)
Put the parenthesis after the # instead of before:
class="small_img_#(Model.line_products[i].short_name)"
I sometimes put a couple of Guids in the id of an element and an underscore separator doesn't work.
There are two ways around this. First use the entity code _ instead and secondly just use a hyphen.
<input id="chk_#classLeader.ClassLeader_#ing.Ingredient.Guid" type="checkbox" class="chk_Done form-check">
<input id="chk-#classLeader.ClassLeader-#ing.Ingredient.Guid" type="checkbox" class="chk_Done form-check">
This is because I want to grab out the Guid's when the check box is clicked with some JQuery like this:
$(".chk_Done").click(function () {
var obj =[];
const itemId = ($(this).attr("id"));
const myArray = itemId.split("_");
var ClassLeaderGuid = myArray[1], IngredientGuid = myArray[2];

Resources