Detect a button and then press it in JavaScript - javascript

I want to make a function that would detect a button on a web page and then click it. But I want it to click a specific item.
function imready()
{
var btn = document.getElementsByClassName('text-xxxs mb-02');
for (var i = 0; i < btn.length; i++)
{
if (btn[i].innerText.indexOf('AK-47') > -1)
{
console.log('runtime');
chrome.runtime.sendMessage({ type: 'dontrun', update: 1 }, function (response) {
});
btn[i].click();
pressok();
}
}
How do I make it so that the var "btn" should equal to document.getElementsbyClassName('x') and also a different className ('y')?

Quoting from https://stackoverflow.com/a/29366682/10450049
getElementsByClassName() returns an HTMLcollection object which is similar to an array but not really an array so you can't call
array methods using the returned value. One hack is to use Array's
prototype methods along with .call()/.apply() to pass the returned
object as the context.
var elems = document.getElementsByClassName("royal") ;
var collapsedElems = document.getElementsByClassName("collapsed");
var earray = Array.prototype.slice.call(elems, 0);
var concatenated = earray.concat.apply(earray, collapsedElems) ;
console.log(concatenated)
Demo Fiddle

As far as i understand your question, you can use document.querySelector('.classX.classY') to select the needed button with both classes.
That works for the case if you only need one button on the page selected, from your code i assume exactly that.

Related

How to read a dynamic string as an object reference

I need to set a button's value as a dynamic string. This string should be turned into an object reference. When the button is clicked the object will be loaded into a new variable. As of right now, it is reading in the string and not reading it as a reference.
Object:
var Wall ={
"Option1":{},
"Option2":{},
"Option3":{
"Option1_3":{
Option1_1_3:{
aTotal:100,
Total_Something_Else:20,
Another_Total:40,
More_totals:20,
Total:20,
},
"Option2_1_3":{},
"Option3_1_3":{}
},
"Option2_3":{},
},
"Option4":{},
"Option5":{}};
Code to create the button:
var options = ['1','2','3','4']
for (var a = 1; a < 2; a++) {
for (var b = 3; b <4; b++) {
for (var i = 0; i < 1; i++) {
document.getElementById('Option1_3').innerHTML = (`<button class="button" value='${JSON.stringify('Wall.Option'+options[i]+'.Option'+a+'_'+b)}' onclick= "PopulateGraph(this.value)">Wall Time</button>`);
}
}
}
function PopulateGraph(val){
console.log(Wall.Option3.Option1_3); //The ouptut of this is what I want
console.log(JSON.parse(val));
}
The above code needs to behave like this piece of code:
Code that works the way I need it to
Output: The top output is what I would need:
Output
Please be mindful that I am very new to coding and javascript. If you have any suggestions at all to make my code better in the long run or have references you think will be helpful please do not be shy. I apologize if this seems like a no-brainer. I have tried a few different solutions to my issue (using eval() and scope[] )and I have yet to figure out a solution. I am giving a very simplified version of my code but the issue is the same.
Use the bracket notation [] to access the properties.
JSON.stringify(Wall['Option'+options[i]]['Option'+a+'_'+b])
Additionally i would create a variable with the text to avoid doing all that work in the same line
for (var i = 0; i < 1; i++) {
const optionValue = Wall['Option' + options[i]]['Option' + a + '_' + b];
const optionJson = JSON.stringify(optionValue);
document.getElementById('Option1_3').innerHTML = (`<button class="button" value='${optionJson}' onclick= "PopulateGraph(this.value)">Wall Time</button>`);
}
At least these issues:
The reference you try to build with i, a and b does not exist in your object. Your object has only a nested object for "Option3", but as i is initialised in the for loop as 0, you are not targeting that property.
The argument passed to JSON.stringify should be the nested value in the object, but you are passing a string, and so that string will be stringified (to yet another string).
I understand you have used single quotes to delimit the value of the value attribute, but (if the previous points are fixed) the targetted value within the Wall object could be a string having a single quote, and then your HTML will still be broken, as that will end the value attribute which you had delimited with single quotes.
As the Wall object is dynamically populated, you risk to be building the buttons too soon -- before the Wall object is populated -- and so the value attributes might still be wrong even when the above issues are resolved.
These complexities can be avoided by not building your button as a HTML string, but using the DOM methods to create the button element together with its attributes.
Furthermore, it seems that the information you store in the value property can be derived dynamically without that attribute:
We can find the parent element of the clicked button, get it's id attribute, and then we know the second-level property in the Wall object.
The Wall object seems to be structured in a way that the first-level property name can be derived from a second-level property name.
Also, you can iterate the properties in the Wall object without the need of a or b integers.
Here is how it could be done:
var Wall ={
"Option1":{},
"Option2":{},
"Option3":{
"Option1_3":{
"Option1_1_3":{
aTotal:100,
Total_Something_Else:20,
Another_Total:40,
More_totals:20,
Total:20,
},
"Option2_1_3":{},
"Option3_1_3":{}
},
"Option2_3": {
"Option2_1_3":{
test:1
}
},
},
"Option4":{},
"Option5":{}
};
for (let option of Object.values(Wall)) {
for (let id in option) {
let container = document.getElementById(id);
if (container) {
let button = document.createElement("button");
button.className = "button";
button.textContent = "Wall Time for " + id;
button.addEventListener("click", PopulateGraph);
container.appendChild(button);
}
}
}
function PopulateGraph() { // no argument.
// Drill down in the Wall object, based on the id of the button's container
let val = this.parentNode.id.match(/\d+/g).reduceRight(
(acc, _, i, arr) => acc[`Option${arr.slice(i).join("_")}`],
Wall
);
console.log(val);
}
<div id="Option1_3"></div>
<div id="Option2_3"></div>

Using parent() in a for loop

I am creating a chrome extension that blocks all porn results on all torrent search engine sites.
So I am trying to retrieve the name of the torrents and check them against the array of strings containing blocked (adult/porn) words that I created. If it matches the array word then it should set the display of the parent element to none. But parent() from jQuery doesn't seem to work around this in a for loop. This is the code that I am using.
// 'blockedWords' is the array.
// '$("dl dt")' contains the words that I am checking against strings from
// the array 'blockedWords'.
for (var i = 0; i < $("dl dt").length; i++) {
for (var j = 0; j < blockedWords.length; j++) {
if($("dl dt")[i].innerText.indexOf(blockedWords[j]) > -1){
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
}
}
}
// 1st Method shows the error 'Cannot set property 'display' of undefined'
// 2nd Method shows the error '$(...)[i].parent is not a function'
// '$("dl dt")[i].parent().style.display' doesn't work but
// '$("dl dt").parent().style.display' doesn't work either
// '$("dl dt")[i].style.display' works perfectly without parent().
I have also tried 'parents()'.
Any help will be appreciated :).
As a newbie, I am also open to any other suggestions or recommendations.
And I would be really grateful if you could explain your code as well :)
And by the way, can you believe there are more than 500 porn companies out there :o :P :D
Since you have jQuery, you can avoid using nested for-loops using jQuery's filter() and JavaScript reduce(s,v):
// Filter function removes elements that return a false/falsey value like 0
$("dl dt").filter(function() {
// Save current element's innerText so we can use it within the reduce function
var str = $(this).text();
// Return sum of reduce function
return blockedWords.reduce(function(s, v) {
// For each item in blockedWords array, check whether it exists in the string. Add to total number of matches.
return s + !!~str.indexOf(v);
}, 0); // 0 = intial value of reduce function (number of matches)
}).parent().hide(); // Hide elements which pass through the filter function
Demo:
var blockedWords = [
'shit', 'fuck', 'sex'
];
$("dl dt").filter(function() {
var str = $(this).text();
return blockedWords.reduce(function(s, v) {
return s + !!~str.indexOf(v);
}, 0);
}).parent().hide();
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<dl><dt>this is shit</dt></dl>
<dl><dt>this is okay</dt></dl>
<dl><dt>fuck this</dt></dl>
<dl><dt>no problem</dt></dl>
<dl><dt>sex videos</dt></dl>
EDIT: I apologize for the earlier answer if you saw it, as it was incomplete. I have also added a snippet for demonstration purposes. For further explanation of the reduce algorithm, check this answer out (basically it converts the value of indexOf to either a 0 or 1, because indexOf returns -1 if not found, or another 0-indexed integer of the position if found).
JQuery's parent function returns a JQuery object with the parent element inside of it. If you want to access the element from this object you need to retrieve the element from the object using the bracket notation.
If you were to provide some HTML I would be able to test this and make sure it works, but here is some code that could get you pointed in the right direction to use mostly JQuery instead of relying on for loops with JavaScript.
JQuery Rewrite
$("dl dt").each(function(index, element){
if($.inArray(blockedWords,$(element).text()) > -1) {
$(this).parent().css("display", "block");
$(element).parent().css("display", "block");
}
})
The Answer To Your Specific Question
Change this:
$(this).parent().style.display= "none"; // 1st Method or
$("dl dt")[i].parent().style.display= "none"; // 2nd Method
to this:
$(this).parent()[0].style.display= "none"; // 1st Method or
$($("dl dt")[i]).parent()[0].style.display= "none"; // 2nd Method
optionally, you can instead use JQuery's css function like this:
$(this).parent().css("display", "none"); // 1st Method or
$($("dl dt")[i]).parent().css("display","none"); // 2nd Method

Is it possible to use more than one changeable divName in getelementbyid?

In this sort of function
function someFunction(divName)
{
document.getElementById(divName).style.someproperty = 'something'
document.getElementById(divName).style.someotherproperty = 'somethingelse'
}
called like this
onClick = "someFunction('someid')
Is there any way to have more than one divName that can be specified in the html?
e.g.
onClick = "someFunction('someid','someotherid')
The first getelementbyid being performed on 'someid' and the second on 'someotherid'
Preferably without jQuery
Edit:
I don't want to do the same thing to each of the two elements, I want to do something different to each individual element, but I want to be able to specify each element in the onClick ="..." instead of in the actual function, so that I don't have to write multiple functions for each combination of two target elements.
Sorry if that was unclear
The Answer (as it turns out, is really simple):
function someFunction(divName,divName2)
{
document.getElementById(divName).style.someproperty = 'something'
document.getElementById(divName2).style.someotherproperty = 'somethingelse'
}
html
onClick = "someFunction('someid','someotherid')"
When I tried it the first time, I wrote "someFunction('someid,someotherid')"
and when it didn't work I assumed that the solution wasn't as easy as divname1,divname2
Sorry, for making all of you run around writing fancy codes.
I assume you want to get an arbitrary number of arguments for a function. You can use the special arguments variable that comes in every called function. It's an array-like object that contains each argument passed into the function, in the order you placed them in the call.
You can do it like this:
function someFunction() {
for(var i = 0; i < arguments.length; i++){
document.getElementById(arguments[i]).style.someproperty = 'something'
}
}
However, classes might be better for this case, assuming you don't mind attaching classes to the target elements.
Give every your div a class name, for example "yourClass". Now you will have a function like this:
function someFunction(divClass) {
var eles = document.getElementsByClassName(divClass);
eles.style.someproperty = 'something';
eles.style.someproperty = 'something';
for(var i = 0; i< eles.length; i++){
// do some thing with each element
eles[i].style.someproperty = 'something';
}
}
Make the parameter an array and loop through it.
function someFunction(divIDs)
{
for (var i = 0; i < divs.length; i++)
{
document.getElementById(divIDs[i]).style.someProperty = 'something';
}
}
What you want to do is give all the elements you want to do something with a class which is the same for all of them, and an ID which is unique to all of them.
function someFunction(e) {
var action = false;
switch(e.target.id) {
case 'someid':
action = 'something';
break;
case 'someotherid':
action = 'somethingelse';
break;
case default:
break;
}
if(!action) return;
document.getElementById(e.target.id).style.someProperty = action;
}
You would then assign the onclick handler to the class:
onClick = "someFunction('theClassName')
If you have more than one element which you want to do the same thing to, instead of using an id, you would add a second class to those elements and just change the function to look for that class.

how to make a class accept a method without specifying the method in advance?

If i have something like this :
function foo(class){
for(i=0; i<(class.length);i++){
return document.getElementsByClassName(class)[i];
}
}
And now i want to do something like this:
foo("someclass").innerHTML="something";
it will do it only for the first element and i understand why this happens and how to make it work correctly, but how can i make the function get other methods without telling it in the loop exactly what to do,like this
foo("someclass").innerHTML="something";//this to put something inside the element
foo("someclass").style.backgroundColor="#000");// and this to work also
So,if its possible,how can i make the function do this without putting these methods in the foo function loop? Is there a way to put these methods in a variable like this
function foo(class).variableMethod{
for(i=0; i<(class.length);i++){
document.getElementsByClassName(class)[i].variableMethod;
}
}
Is this possible?
You can pass a function to foo(), and have foo() call that function with each matched element:
function foo(className, func)
{
var elements = document.getElementsByClassName(className);
for (var i = 0; i < elements.length; ++i) {
func(elements[i]);
}
}
Now you can do something like:
foo("someclass", function(element) {
element.innerHTML = "something";
element.style.backgroundColor = "#000";
});
Just to add to the other great answers here, one thing you could do is create on override method called innerHtml. You could then have innerHtml check for a collection as well as a single instance and opperate as required.
UPDATE
Here's a quick example of method chaining. Here I basically obtain a collection of elements based on tag name and set their value using a val() method that I created using the Javascript prototype object.
function Foo(tagName)
{
this.collection = document.getElementsByTagName(tagName);
return this;
}
Foo.prototype.val= function(value){
for(var i = 0; i < this.collection.length; i++)
{
this.collection[i].innerHTML = value;
}
}
var x = new Foo("li").val("Hello World");
You can see a working demo of this here: http://jsfiddle.net/E48ym/1/
Frédéric Hamidi's answer will do what you want I think, but you could also use a framework like jQuery - this is the kind of thing it was designed for. So you could do:
$('.className').text('something');
to set the text of all elements with a class of className to something, or
$('.className').css('background-color', '#000');
to change the background color of all elements with a class of className.

Is there a way to get jQuery selector expression text?

I have an array of selectors like :
var arr = [".discuss .title .post", ".post .desc" , ".eventlist .event"];
I want to loop through this array and attach a click event on it.
for(var i in arr){
$(arr[i]).click(function(){
//here I need the selector i.e. arr[i] expression - for some css work
});
}
Is there a way by which I can get this selector expression inside the click callback function?
I went through this post which has similar problem : How do I get a jQuery selector's expression as text?
But as said there, I couldn't find any "selector" attribute of jQuery object. I tried this way:
for(var i in arr){
$(arr[i]).click(function(){
console.log(jQuery(this).attr('selector')); //This gives undefined
});
}
Any help?
The correct syntax is $('.something').selector. However, .selector only works when a selector string is explicitly supplied - but there is no selector in $(this).
One workaround is to save a copy of the selector in a function surrounding the click function:
for (var i = 0; i < arr.length; i++) { // (Don't use "for-in loop" for arrays)
(function (selector) { // 2. a copy is saved as the argument "selector"
$(selector).click(function () {
console.log(selector);
});
}) (arr[i]); // 1. Pass in the selector
}
Another option is to compare $(this) to each selector in your array, using .is():
$(arr.join(',')).click(function () { // a quick way to select all selectors
var selector;
for (var i = 0; i < arr.length; i++) {
if ($(this).is(arr[i])) {
selector = arr[i];
break;
}
}
console.log(selector);
});
You can at least get the class value of each selected element:
$(this).attr("class");
Doesn't this provide sufficient info for what you intend to do?
I don't seem to get any error, its as though there is no .click() event, example here
But if you split your array and attach a click to every item in your array it seems to work for me.
// Trouble is you will attach two click's to a class="post" item:
var arr = [".discuss .title .post", ".post .desc" , ".eventlist .event"];
for(var a in arr){
var ar = arr[a].split(' ');
for(var i in ar){
$(ar[i]).click(function(e){
e.preventDefault();
var href = $(this).attr('href');
var sele = $(this).attr('selector');
console.log(href);
console.log(sele);
alert("HREF: "+href+" - Selector: "+sele);
});
}
}
Take a look at it in action here along with a solution to stopping adding two clicks.

Categories