Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EQCSS is not defined #96

Closed
youradds opened this issue Nov 21, 2018 · 14 comments
Closed

EQCSS is not defined #96

youradds opened this issue Nov 21, 2018 · 14 comments

Comments

@youradds
Copy link

Hi,

I can't get this to work. I can see the script loading in the timeline, but if I try and do:

EQCSS.apply()

I get an error. Looking on here you say you try running EQCSS in the Firebug console line. I run it, and I get:

ReferenceError: EQCSS is not defined

What am I doing wrong?

Thanks

Andy

@tomhodgins
Copy link
Contributor

If EQCSS is not defined you can be sure that the JavaScript has not properly or fully loaded. Just typing EQCSS into the console should return the plugin object if EQCSS.js has successfully loaded.

<script src=https://elementqueries.com/EQCSS.min.js></script>
<script>alert(EQCSS.version)</script>

Here's a basic HTML test that should alert the currently loaded EQCSS version number. If this works for you, but not how you're currently loading the plugin, there must be some kind of problem with the way you've loaded the JavaScript, or where you're trying to access it from or something.

Do you have an example of the site online anywhere that we could troubleshoot or debug it?

@youradds
Copy link
Author

youradds commented Nov 21, 2018

Hi @tomhodgins . Thanks for the reply :) I slapped your code into my page, and then removed the other section I had to load it via requireJS. When I run the page I now get:

https://ibb.co/krfqcA

I'm afraid the page in question isn't public facing (we have it behind an IP firewall to stop dup content coming up, as its our dev server). If you are happy to email me your IP on andy.newby@gmail.com, then I'm more than happy to add your IP to the firewall so you can see it :)

Interestingly it works fine on a fiddle: http://jsfiddle.net/youradds/om60w4zb/

Thanks

Andy

@tomhodgins
Copy link
Contributor

Ah, that makes me wonder if it's something to do with client side 'module' loading! EQCSS is wrapped in a UMD wrapper to attempt to let it be handled by various different module loaders while still working out-of-the-box in the browser if no module loader is found. I believe we used a version of this template -> https://github.com/umdjs/umd/blob/master/templates/amdWebGlobal.js

Is it possible to load EQCSS separately from the other module loading, and link it into your HTML before define() gets defined?

Alternatively, what if you manually 'unwrap' the EQCSS plugin code from its current wrapper, I'm not sure how you're loading the other frontend code, but taking EQCSS out of its wrapper and packaging it up similar to your other code should work, the plugin itself doesn't take advantage of any modular functionality inside the plugin so it should be fine run as a normal global JS plugin :D

@youradds
Copy link
Author

@tomhodgins you star! That was it. I removed the wrapper code for it, and now it correctly prints out the version number as an alert()

The other thing I was a bit confused about was how to generate them with SASS. I've seen people talking about it, but I couldn't find any mixin's for it? (I found a reference to some _raw() functions, but that didn't do it for me). If there is a "proper" SASS template functionality I could use, that'd be awesome 👍

Thanks

Andy

@tomhodgins
Copy link
Contributor

Yay! Glad that worked :D

I've seen a number of different Sass mixins that kind of quote code to pass through, but I am not sure it makes sense to do extra work in your preprocessor for the purpose of passing through code that will be parsed by something else later. I'd recommend breaking out the EQCSS-powered styles from the Sass and handling them separately.

More info about EQCSS + preprocessors here: https://github.com/eqcss/eqcss/wiki/Using-EQCSS-with-CSS-Preprocessors including a link to one of the mixins I've seen floating around for Sass

@youradds
Copy link
Author

youradds commented Nov 21, 2018

Awesome thanks. I think you may be right about taking it out of the SASS stuff. Its a bit of a niche thing I'm needing to do inside a modal (obviously MQ's don't work when you have a fixed size modal that only gets smaller at x pixels)

I've added this test code in:

@element #main-suggestion-wrapper .col and (max-width: 300px) {
	$this { background: red; }
}

My understanding is that it should make the background red for #main-suggestion-wrapper .col when its <= 300px wide - but it doesn't seem to be doing it. Could it be because I'm pulling the modal in from another source, and then injecting it into the DOM with:

document.getElementById("booking-date-form").innerHTML =

+ data +
;

Maybe I need to do it another way for it to work?

I'm just heading out for the evening. Thanks for your help so far. Much appreciated! ( I was going round and round in circles with this earlier, trying to figure out what I was doing wrong)

Thanks

Andy

@tomhodgins
Copy link
Contributor

tomhodgins commented Nov 21, 2018

It should be okay. If you're wondering what styles EQCSS has successfully parsed and loaded you can peek inside the EQCSS.data array in your browser's console. You should see the rule(s) EQCSS has loaded:

screen shot 2018-11-21 at 12 31 06 pm

If this is good, you're golden so far. It doesn't matter what HTML is loaded, EQCSS knows about this rule.

Suppose you have markup that doesn't exist on the page at first, but at some point you add more markup. EQCSS will normally apply on click, resize, and input events (and so the next time any of these happen it should apply) but if you want to be sure that EQCSS rules apply to HTML as soon as it's added to the document, you might want to run EQCSS.apply() after the new HTML has been added.

Here's a little demo where I store some HTML that your style would apply to in a <template> tag so it's not on the page at the time the EQCSS parses and loads the rule. If later you were to add the HTML to the page, run EQCSS.apply() and the parsed EQCSS rules will apply to it. (I changed the breakpoint to 800px just to make it easier to test)

<template>
  <div id=main-suggestion-wrapper>
    <div class=col>I'm a test!</div>
  </div>
</template>

<style>
  @element #main-suggestion-wrapper .col and (max-width: 800px) {
    $this { background: red; }
  }
</style>

<script src=https://elementqueries.com/EQCSS.min.js></script>
<script>
  // Add HTML from <template> to <body> after 1 second
  setTimeout(() => {
    const template = document.querySelector('template')
    Array.from(template.content.children).forEach(child => 
      document.body.appendChild(child)
    )
    EQCSS.apply()
  }, 1000)
</script>

If this demo didn't have EQCSS.apply() there, you would need to click or resize or type in the browser to trigger the next automatic recalculation for the styles to apply. Hopefully this is what you're looking for :D

@youradds
Copy link
Author

Thanks for that Interestingly I get:

EQCSS.data []
	length: 0
	<prototype>: Array []

I have done the same test as you:

@element #main-suggestion-wrapper .col and (max-width: 800px) {
	$this { background: red; }
}

Am I correct in thinking the rules should be loaded into EQCSS.data when loaded? (not when they are needed)

I expect you are in bed now, so I will carry on playing with it. Hopefully I can resolve it :)

Thanks

Andy

@youradds
Copy link
Author

OK I'm getting closer. It seems it doesn't like it when the CSS is loaded dynamically:

function addCSS(url) {
    var cacheBuster = 1;
    var fileref=document.createElement("link");
    fileref.setAttribute("rel", "stylesheet");
    fileref.setAttribute("type", "text/css");
    if (cacheBuster == 1) {
        fileref.setAttribute("href", url + "?x="+ new Date());
    } else {
        fileref.setAttribute("href", url);
    }

    document.getElementsByTagName("head")[0].appendChild(fileref);
}

Shouldn't it detect a new CSS file was added, and then process that as well? If I change it to a static tag in the HTML page it works fine:

<link rel="stylesheet" href="/2018/css/resa-booking-modal.css" />

Gives me:

	0: {…}
		conditions: Array [ {…} ]
			selector: "#main-suggestion-wrapper .col"
			style: " $this { background: red; } "
			<prototype>: Object { … }
	length: 1
	<prototype>: Array []```

I guess this is less relevant if I move the EQCSS into its own **.eqcss** template file anyway - but I'm just interested to know if it SHOULD work as I'm expecting?

Thanks!

Andy

@youradds
Copy link
Author

BTW I wonder if its possible in a future version to allow people to decide what they want to actually process? i.e there is little point processing inline styles or .css files, if all the work is in the .css files. For me I have just commented out:

      // Retrieve all link tags
      // var link = document.getElementsByTagName('link')

      // for (i = 0; i < link.length; i++) {

      //   // Test if the link is not read yet, and has rel=stylesheet
      //   if (link[i].getAttribute('data-eqcss-read') === null && link[i].rel === 'stylesheet') {

      //     // retrieve the file content with AJAX and process it
      //     if (link[i].href) {

      //       (function() {

      //         var xhr = new XMLHttpRequest

      //         xhr.open('GET', link[i].href, true)
      //         xhr.send(null)
      //         xhr.onreadystatechange = function() {

      //           if (xhr.readyState === 4 && xhr.status === 200) {

      //             EQCSS.process(xhr.responseText)

      //           }

      //         }

      //       })()

      //     }

      //     // Mark the link as read
      //     link[i].setAttribute('data-eqcss-read', 'true')

      //   }

      // }

I'm going to put all the stuff in the .eqcss files to keep it tidy (as your suggested), so there is no point processing the .css / <style> bits. Maybe you could put some options at the top, so people could update the flags for .eqcss / <style> / .css processing?

@tomhodgins
Copy link
Contributor

Am I correct in thinking the rules should be loaded into EQCSS.data when loaded? (not when they are needed)

Yes, any rules that have been parsed or loaded into the EQCSS.data array by the plugin will be here, and the plugin will be watching the page each time it reprocesses to decide when to apply any of the rules it knows about.

It seems it doesn't like it when the CSS is loaded dynamically
Shouldn't it detect a new CSS file was added, and then process that as well?

You can run EQCSS.load() at any time to have it look through the document for any <style>, <link> or <script type=text/eqcss> tags that it hasn't parsed yet. This is good if you're adding CSS to the page.

If you are adding only EQCSS to the page, you can also parse and load the code via JS directly to get it into EQCSS.data without doing through the effort of putting it in a tag and having the plugin look for tags it hasn't read yet.

You can also use EQCSS.parse() to parse EQCSS syntax into the data format you see inside EQCSS.data, and you can do this server-side with eqcss-parse, and if you have a string of EQCSS syntax you want to parse + register, there's a helper function called EQCSS.process() which will do both for you at the same time :D

If you're working with the EQCSS syntax as a string in JS you can get it right into the plugin without it ever existing in the DOM.

BTW I wonder if its possible in a future version to allow people to decide what they want to actually process? i.e there is little point processing inline styles or .css files

As EQCSS parses styles from a tag it adds a data-eqcss-read="true" attribute. Any tag that has this attribute on it at the time the page loads will be completely skipped by EQCSS's parsing.

@youradds
Copy link
Author

Hi,

Cool thanks :) so if I added data-eqcss-read="true" to the CSS tag, it wouldn't get processed?

Cheers

Andy

@tomhodgins
Copy link
Contributor

I added data-eqcss-read="true" to the CSS tag, it wouldn't get processed?

Yes, put that on any <style>, <link>, or <script type=text/eqcss> you want the EQCSS.js plugin to skip over when it searches the DOM for styles to read.

Since EQCSS I've continued working on a number of different plugins that all solve similar issues. Lately the performance of jsincss and some of its plugins can be 10x faster than similar functionality in EQCSS. I'll show a few examples :D

First we'll start with an EQCSS example. Here I have a <div> and 1 element query written for min-width, and a breakpoint of 800px. To run this I've pulled in the EQCSS plugin and had it parse and read a custom syntax that looks kind of like CSS in order to extract the values the JS plugin needs to know about so it can run it.

<div>EQCSS</div>

<script src=https://unpkg.com/eqcss/EQCSS.js></script>
<script>
  EQCSS.process(`
    @element div and (min-width: 800px) {
      :self {
        background: lime;
      }
    }
  `)
</script>

We can eliminate the need for reading the custom style if we write @element div and (min-width: 800px) {} as something like element('div', {minWidth: 800}, "") and having a JavaScript function run every time the styles need to be recalculated. A function like this can return the CSS that needs to exist as a string of CSS. Here I'm using jsincss directly:

<div>jsincss + jsincss-element-query</div>

<script type=module>
  import jsincss from 'https://unpkg.com/jsincss/index.vanilla.js'
  import element from 'https://unpkg.com/jsincss-element-query/index.vanilla.js'

  jsincss(() => `
    ${element('div', {minWidth: 800}, `
      [--self] {
        background: lime;
      }
    `)}
  `)
</script>

And if you squint your eyes it kinda sorta looks something like our EQCSS syntax before, but instead of writing CSS you're writing JavaScript. The other benefit is that you have easy flexibility of what functionality you want to use. EQCSS has element queries, extra selectors, JS evaluation in CSS, element based units, etc, and you don't always need all of the features. With jsincss you can make individual functions for each feature you want to use and load only the parts that matter. That's where part of the performance increase comes from.

But, just this summer I learned how we could express this in valid CSS, so you can write a valid CSS stylesheet that contains the information you otherwise would have put in EQCSS. You can preprocess it server-side with qaffeine or client-side in the browser with deqaf and extract the values that jsincss and its plugins are looking for. Here's the same example, except instead of a custom script type I'm writing the information in pure CSS, in a way that the browser will ignore, but JS can read and act on. This allows you to run code very similar to the previous jsincss example, only you're writing CSS instead of Javascript. And it still resembles EQCSS if you squint your eyes!

<div>deqaf + jsincss-element-query</div>

<style>
  @supports (--element("div", {"minWidth": 800})) {
    [--self] {
      background: lime;
    }
  }
</style>

<script type=module>
  import deqaf from 'https://unpkg.com/deqaf/index.js'
  import element from 'https://unpkg.com/jsincss-element-query/index.vanilla.js'

  deqaf({stylesheet: {element}})
</script>

So there are a lot of different ways you can express the same kind of logic, and ways you can easily use your own functionality for styling!

@youradds
Copy link
Author

Hi,

Wow thanks for the detailed reply! I will have a look into the tweaking of specific features for what we need. TBH I'm only processing a short file with a few rules in - so it may be better to run the EQCSS.process() method inline in the JS, so there is no need for the extra .eqcss file (seeing as its only a few lines)

Thanks again - I really appreciate the time and effort that you've put into this plugin 💯

Cheers

andy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants