My favourite bits of JS.
This image is a combination of AI and PowerPoint. I rather like it.

My favourite bits of JS.

You know when you sidle up to someone in the street or accost them at a party, and ask: "What's your favourite bit of JavaScript?"

Well... the reaction you get is why I restrict this sort of behaviour to LinkedIn.

querySelectorAll

Here's the scenario - the data layer is sparse / non existent. You need to get some data off a page. Sure you've got your classics like getElementById() and so on, but what if the elements on the site don't have a an ID? I like to use querySelectorAll, as I find it super flexible.

Let's go to loophorizon.com and look at this image-of-a-person-I-don't-know half way down the page:

I don't know the people in the background either... and our office is a lot nicer...

The image does't have an ID (and let's pretend it doesn't have a clearly defined class for the purpose of this example); what if I want to pull the image source?

querySelectorAll() to the rescue!

document.querySelectorAll('[id="w-node-_6f5fdede-377f-4852-6bd7-dfa14675aae4-ee8628a5"]');        

But wait - that's the div element that contains the image! Never fear...

document.querySelectorAll('[id="w-node-_9eb7db44-e590-4e8e-7625-bb378dfdb91b-ee8628a5"]')[0].getElementsByTagName('img')[0].src;        

Ok, so you could use querySelector() instead of querySelectorAll() (and avoid all that [0] shenanigans) if you know the parent element has a unique ID. And OK, you could use getElementByID() and then perform the same drill down, but here's the thing - you can use querySelectorAll() on any element attribute!

Let's look at the Loop Horizon logo (and again, let's pretend it does't have a clearly defined class for the purpose of this example):

I could use any of the following to grab the image source:

document.querySelectorAll('[data-w-id="cece7bb6-e93f-1599-2f86-22ac2b9f0a7d"]')[0].getElementsByTagName('img')[0].src;

//OR

document.querySelectorAll('[aria-current="page"]')[0].getElementsByTagName('img')[0].src;

//OR

document.querySelectorAll('[style="will-change: opacity; opacity: 1;"]')[0].getElementsByTagName('img')[0].src;        

And here's the real key as to why I love querySelectorAll() - it returns a NodeList (all the [0] shenanigans), and that means you can do stuff like this:

document.querySelectorAll('[class^="container-"]');        

Yep, that's regex. Some of those IDs are a bit long winded, or maybe you just want to pull all elements within a certain section of the page and see what's in there; the fact that querySelectorAll() can use regex and the fact that it returns a NodeList, gives that flexibility I talked about; return as little or as much as you like, and then do what you want with it!

Array functions

That brings be nicely onto my next favourite bit of JS. Array functions.

You've got your node list, but how do you get the information you want from it? It's all well and good using document.querySelectorAll('[data-w-id="cece7bb6-e93f-1599-2f86-22ac2b9f0a7d"]')[0].getElementsByTagName('img')[0].src; (for example), but what if the 'img' isn't in position [0]? Or what if there's more than one 'img' being used?

Let's turn our NodeList into an array, so we can start to get funky with it!

Array.from(document.querySelectorAll('[class^="container-"]'));        

Now it's an array, we can filter down to the bits of that array that contain images!

Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return key.getElementsByTagName('img')});

//OR

Array.from(document.querySelectorAll('[class^="container-"]')).map(key => key.getElementsByTagName('img'));

//I prefer the verbose first version just because it makes more sense in my head... plus it works in ES5. I know... I'm a dinosaur.        

Ah the map() function - it creates a new output array from the input array (and you have to input an array[]), based on the conditions stipulated.

I love it... a lot. Maybe too much, but I'm only human.

But oh no though! We get a bunch of HTML objects...

Well wrap your eyes around this...

Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return Array.from(key.getElementsByTagName('img'))});

//OR

Array.from(document.querySelectorAll('[class^="container-"]')).map(key => Array.from(key.getElementsByTagName('img')));        

That's right - just use the Array.from() function again, nested within the map(), to return nested arrays instead.

Now we can use another array function to filter out any empty arrays returned.

Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return Array.from(key.getElementsByTagName('img'))}).filter(function(key){return key.length > 0});

//OR

Array.from(document.querySelectorAll('[class^="container-"]')).map(key => Array.from(key.getElementsByTagName('img'))).filter(key => key.length > 0);        

So that's sorted that out.

Now to return the image source - for this, we need to tweak the getElementsByTagName('img') code to return the source rather than the full image object - using another array function of course, given there may be more than one image per element.

Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return Array.from(key.getElementsByTagName('img')).map(function(key){return key.src})}).filter(function(key){return key.length > 0});

//OR

Array.from(document.querySelectorAll('[class^="container-"]')).map(key => Array.from(key.getElementsByTagName('img')).map(key => key.src)).filter(key => key.length > 0);        

But alas there are some nested arrays. We can sort that using the concat function though.

[].concat.apply([],Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return Array.from(key.getElementsByTagName('img')).map(function(key){return key.src})}).filter(function(key){return key.length > 0}));

//OR

[].concat.apply([],Array.from(document.querySelectorAll('[class^="container-"]')).map(key => Array.from(key.getElementsByTagName('img')).map(key => key.src)).filter(key => key.length > 0));        

And now we have all the image sources associated with the "class" beginning with "container-".

Amazing... but kind of useless, as it stands. What shall we do with it?

Let's pretend that every single one of the images relates to a promotion, and wrap this whole ordeal up by pushing each image source onto the dataLayer as part of a view_promotion event to get hypothetically collected by GA4 - https://developers.google.com/analytics/devguides/collection/ga4/reference/events?client_type=gtm#view_promotion.

[].concat.apply([],Array.from(document.querySelectorAll('[class^="container-"]')).map(function(key){return Array.from(key.getElementsByTagName('img')).map(function(key){return key.src})}).filter(function(key){return key.length > 0})).forEach(function(key){dataLayer.push({event: "view_promotion",
  ecommerce: {
    creative_name: key,
    creative_slot: "example_position",
    promotion_id: "fake_id",
    promotion_name: "fake_name",
    items: []
  }
})});        

Obviously, this is over-simplified; in this hypothetical scenario it's likely that unique promotion detail (for each image / group of images) would be available somewhere within the various elements returned by the original "class"-beginning-with-"container-" querySelectorAll() function. And you could therefore (of course) tweak the various map functions to pull this data out at the correct stage... and maybe use one of my other favourite bits of JavaScript - Object.assign() - by first creating a core promotion level object per element returned, and then iterating through the list of image sources within each element to Object.assign() it back to the core object.

But this example goes to show how another simple array function - forEach - can be used to iterate through the array of image sources generated and do something usefuly with them.

END.

We all have JQuery to thank for querySelectorAll

David Henderson

Principal App Developer at Interactive Investor

1 年

As a developer, I promise to keep inserting random data attributes onto the DOM, only to have them randomise on each deploy. We can't make it too easy for you ?? (Love a good .map)

Alban Gér?me

Founder, SaaS Pimp and Automation Expert, Intercontinental Speaker. Not a Data Analyst, not a Web Analyst, not a Web Developer, not a Front-end Developer, not a Back-end Developer.

1 年

Array functions chaining, closely followed by ternary operators. They have revolutionised how I code in Javascript. You might be able to replace this: [].concat.apply([],Array.from(document.querySelectorAll('[class^="container-"]')).map(someFunction) ... with: [].map.call(document.querySelectorAll('[class^="container-"]'), someFunction I know Array.from, it's a fairly recent addition to Javascript, but I almost never used it. I use [].map.call instead.

要查看或添加评论,请登录

Matt Bentley的更多文章

  • Improving cookie persistence (using a reverse proxy on GCP) - AKA: what I do in my spare time

    Improving cookie persistence (using a reverse proxy on GCP) - AKA: what I do in my spare time

    That title has some words in it. Even I'm not sure they form a coherent sentence, but it was the best I could do for…

    12 条评论
  • Integrating cookie management with tag management platforms

    Integrating cookie management with tag management platforms

    A few people have spoken to me recently about issues with race conditions when trying to integrate their cookie…

    4 条评论
  • Event augmentation

    Event augmentation

    Introduction It makes sense to try to keep your data layer as simple and as granular as possible. Simple; so it’s easy…

    1 条评论
  • Get the most out of GA4: 6 Things to Do Now You’ve Migrated

    Get the most out of GA4: 6 Things to Do Now You’ve Migrated

    Congratulations on successfully migrating to Google Analytics 4 (GA4)! Now that you’ve made the transition, it’s time…

    2 条评论
  • Testing site speed

    Testing site speed

    We all know there’s an important correlation between site speed and important KPIs like conversion and customer…

    2 条评论
  • Data Layers

    Data Layers

    At Loop Horizon, we talk about drawing a direct line between customer intelligence and customer experience - e.g.

    3 条评论
  • Your baby ain't special...

    Your baby ain't special...

    I’ve just had a baby, and I must admit, if someone said that to me, I’d be pretty mad. But I’ve got to face facts; soon…

    4 条评论
  • Anyone for real-time data?

    Anyone for real-time data?

    I've been to a couple of conferences recently where there's been a lot of chat – both on and off stage – about…

    16 条评论
  • Optimisation vs Personalisation

    Optimisation vs Personalisation

    At Sky I’ve been lucky enough to have been part of a fantastic optimisation team AND played a major role in delivering…

    12 条评论
  • Why bother collecting clickstream data?

    Why bother collecting clickstream data?

    Why bother collecting clickstream data? Good question. To answer a question with another question: What are you going…

    10 条评论

社区洞察

其他会员也浏览了